+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/baseinfo.html.php b/module/instance/view/baseinfo.html.php
new file mode 100644
index 0000000000..bcdf7c9640
--- /dev/null
+++ b/module/instance/view/baseinfo.html.php
@@ -0,0 +1,115 @@
+
+solution) && array_key_exists(strtolower($instance->chart), $this->config->instance->seniorChartList)):?>
+
diff --git a/module/instance/view/custominstall.html.php b/module/instance/view/custominstall.html.php
new file mode 100644
index 0000000000..6571490d43
--- /dev/null
+++ b/module/instance/view/custominstall.html.php
@@ -0,0 +1,69 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/editname.html.php b/module/instance/view/editname.html.php
new file mode 100644
index 0000000000..63fe7dde60
--- /dev/null
+++ b/module/instance/view/editname.html.php
@@ -0,0 +1,28 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/install.html.php b/module/instance/view/install.html.php
new file mode 100644
index 0000000000..f187c398cf
--- /dev/null
+++ b/module/instance/view/install.html.php
@@ -0,0 +1,118 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.cn
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+instance->notices);?>
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/resourceerror.html.php b/module/instance/view/resourceerror.html.php
new file mode 100644
index 0000000000..42334cf10d
--- /dev/null
+++ b/module/instance/view/resourceerror.html.php
@@ -0,0 +1,22 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.cn
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/restoresettings.html.php b/module/instance/view/restoresettings.html.php
new file mode 100644
index 0000000000..02412b1f31
--- /dev/null
+++ b/module/instance/view/restoresettings.html.php
@@ -0,0 +1,55 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.cn
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+app->getModuleRoot() . '/common/view/datepicker.html.php';?>
+instance->notices);?>
+id);?>
+
+
+
+
instance->restore->autoRestore;?>
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/tosenior.html.php b/module/instance/view/tosenior.html.php
new file mode 100644
index 0000000000..349ca35546
--- /dev/null
+++ b/module/instance/view/tosenior.html.php
@@ -0,0 +1,28 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.cn
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+
+
instance->toSeniorAttention;?>
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/upgrade.html.php b/module/instance/view/upgrade.html.php
new file mode 100644
index 0000000000..0aa14ee7f5
--- /dev/null
+++ b/module/instance/view/upgrade.html.php
@@ -0,0 +1,32 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+instance->notices);?>
+
+
+
instance->upgrade} : {$instance->name}";?>
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/instance/view/view.html.php b/module/instance/view/view.html.php
new file mode 100644
index 0000000000..83511316be
--- /dev/null
+++ b/module/instance/view/view.html.php
@@ -0,0 +1,50 @@
+
+ * @package instance
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+instance->notices);?>
+id));?>
+demoAppLife);?>
+
+
+
name;?>
+ appVersion;?>
+ createLink('instance', 'editname', "id=$instance->id", '', true), '', '', "class='iframe' title='$lang->edit' data-width='600' data-app='space'");?>
+ solution):?>
+ solution"), $instance->solutionData->name, '', "class='label label-success label-outline solution-link'");?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/system/config/dtable.php b/module/system/config/dtable.php
new file mode 100644
index 0000000000..6a1d9c9d77
--- /dev/null
+++ b/module/system/config/dtable.php
@@ -0,0 +1,50 @@
+loadLang('instance');
+
+$config->system->dtable = new stdclass();
+
+$config->system->dtable->dbList = new stdclass();
+
+$config->system->dtable->dbList->fieldList['name']['title'] = $lang->system->dbName;
+$config->system->dtable->dbList->fieldList['name']['group'] = 1;
+
+$config->system->dtable->dbList->fieldList['dbType']['title'] = $lang->system->dbType;
+$config->system->dtable->dbList->fieldList['dbType']['name'] = 'db_type';
+$config->system->dtable->dbList->fieldList['dbType']['group'] = 2;
+
+$config->system->dtable->dbList->fieldList['dbStatus']['title'] = $lang->system->dbStatus;
+$config->system->dtable->dbList->fieldList['dbStatus']['name'] = 'status';
+$config->system->dtable->dbList->fieldList['dbStatus']['map'] = $lang->instance->statusList;
+
+$config->system->dtable->dbList->fieldList['actions']['name'] = 'actions';
+$config->system->dtable->dbList->fieldList['actions']['title'] = $lang->actions;
+$config->system->dtable->dbList->fieldList['actions']['type'] = 'actions';
+$config->system->dtable->dbList->fieldList['actions']['menu'] = array('management');
+
+$config->system->dtable->dbList->fieldList['actions']['list']['management']['icon'] = 'cog-outline';
+$config->system->dtable->dbList->fieldList['actions']['list']['management']['hint'] = $lang->system->management;
+
+$config->system->dtable->backup = new stdclass();
+
+$config->system->dtable->backup->fieldList['time']['title'] = $lang->system->backup->time;
+$config->system->dtable->backup->fieldList['time']['type'] = 'datetime';
+
+$config->system->dtable->backup->fieldList['backupPerson']['title'] = $lang->system->backup->backupPerson;
+
+$config->system->dtable->backup->fieldList['type']['title'] = $lang->system->backup->type;
+
+$config->system->dtable->backup->fieldList['actions']['name'] = 'actions';
+$config->system->dtable->backup->fieldList['actions']['title'] = $lang->actions;
+$config->system->dtable->backup->fieldList['actions']['type'] = 'actions';
+$config->system->dtable->backup->fieldList['actions']['menu'] = array('restore', 'delete');
+
+$config->system->dtable->backup->fieldList['actions']['list']['restore']['icon'] = 'history';
+$config->system->dtable->backup->fieldList['actions']['list']['restore']['hint'] = $lang->system->backup->restore;
+$config->system->dtable->backup->fieldList['actions']['list']['restore']['url'] = array('module' => 'backup', 'method' => 'restore', 'params' => 'time={time}');
+$config->system->dtable->backup->fieldList['actions']['list']['restore']['data-confirm'] = $lang->system->backup->confirmDelete;
+
+$config->system->dtable->backup->fieldList['actions']['list']['delete']['icon'] = 'trash';
+$config->system->dtable->backup->fieldList['actions']['list']['delete']['hint'] = $lang->delete;
+$config->system->dtable->backup->fieldList['actions']['list']['delete']['url'] = array('module' => 'backup', 'method' => 'delete', 'params' => 'time={time}');
+$config->system->dtable->backup->fieldList['actions']['list']['delete']['data-confirm'] = $lang->system->backup->confirmRestore;
diff --git a/module/system/control.php b/module/system/control.php
new file mode 100644
index 0000000000..9ffe0cb106
--- /dev/null
+++ b/module/system/control.php
@@ -0,0 +1,699 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+class system extends control
+{
+
+ /**
+ * __construct
+ *
+ * @access public
+ * @return void
+ */
+ public function __construct($moduleName = '', $methodName = '')
+ {
+ parent::__construct($moduleName, $methodName);
+
+ $this->backupPath = $this->loadModel('backup')->getBackupPath();
+ if(!is_dir($this->backupPath))
+ {
+ if(!mkdir($this->backupPath, 0755, true)) $this->view->error = sprintf($this->lang->system->backup->error->noWritable, $this->backupPath);
+ }
+ else
+ {
+ if(!is_writable($this->backupPath)) $this->view->error = sprintf($this->lang->system->backup->error->noWritable, $this->backupPath);
+ }
+ if(!is_writable($this->app->getTmpRoot())) $this->view->error = sprintf($this->lang->system->backup->error->noWritable, $this->app->getTmpRoot());
+
+ $this->loadModel('action');
+ $this->loadModel('setting');
+ }
+
+ /**
+ * System index.
+ *
+ * @access public
+ * @return void
+ */
+ public function index()
+ {
+ $this->view->title = $this->lang->system->common;
+
+ $this->display();
+ }
+
+ /**
+ * Dashboard page.
+ *
+ * @param int $total
+ * @param int $recPerPage
+ * @param int $pageID
+ * @access public
+ * @return void
+ */
+ public function dashboard($total = 0, $recPerPage = 20, $pageID = 1)
+ {
+ $this->loadModel('cne');
+ $this->app->loadClass('pager', true);
+ $pager = new pager($total, $recPerPage, $pageID);
+
+ $instances = $this->loadModel('instance')->getByAccount($this->app->user->account, $pager, '', '', 'running');
+
+ $actions = $this->loadModel('action')->getDynamic('all', 'today');
+
+ $this->view->position[] = $this->lang->my->common;
+
+ $this->view->title = $this->lang->my->common;
+ $this->view->instances = $instances;
+ $this->view->actions = $actions;
+ $this->view->cneMetrics = $this->cne->cneMetrics();
+ $this->view->instancesMetrics = $this->cne->instancesMetrics($instances);
+ $this->view->pager = $pager;
+
+ $this->display();
+ }
+
+ /**
+ * Show database list.
+ *
+ * @access public
+ * @return void
+ */
+ public function dbList()
+ {
+ $this->app->loadLang('instance');
+
+ $this->view->title = $this->lang->system->dbManagement;
+ $this->view->dbList = $this->loadModel('cne')->allDBList();
+
+ $this->display();
+ }
+
+ /**
+ * Test the connection of LDAP by post parameters.
+ *
+ * @access public
+ * @return void
+ */
+ public function testLDAPConnection()
+ {
+ $settings = fixer::input('post')
+ ->setDefault('host', '')
+ ->setDefault('port', '')
+ ->setDefault('bindDN', '')
+ ->setDefault('bindPass', '')
+ ->setDefault('baseDN', '')
+ ->get();
+
+ $success = $this->system->testLDAPConnection($settings);
+ if($success) return $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->verifyLDAPSuccess));
+
+ return $this->send(array('result' => 'fail', 'message' => $this->lang->system->errors->verifyLDAPFailed));
+ }
+
+ /**
+ * Install LDAP
+ *
+ * @access public
+ * @return void
+ */
+ public function installLDAP()
+ {
+ if($this->system->hasSystemLDAP()) return print(js::locate($this->inLink('ldapView')));
+
+ $channel = $this->app->session->cloudChannel ? $this->app->session->cloudChannel : $this->config->cloud->api->channel;
+ $ldapApp = $this->loadModel('store')->getAppInfoByChart('openldap', $channel, false);
+ if($_POST)
+ {
+ $postData = fixer::input('post')->setDefault('source', 'qucheng')->get();
+ if($postData->source == 'qucheng')
+ {
+ $this->system->installQuchengLDAP($ldapApp, $channel);
+ }
+ else if($postData->source == 'extra')
+ {
+ $this->system->configExtraLDAP((object)$postData->extra);
+ }
+ else
+ {
+ dao::$errors[] = $this->lang->system->notSupportedLDAP;
+ }
+
+ if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
+
+ $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->ldapInstallSuccess, 'locate' => $this->inLink('ldapView')));
+ }
+
+ $this->lang->switcherMenu = $this->system->getLDAPSwitcher();
+
+ $this->view->title = $this->lang->system->ldapManagement;
+ $this->view->ldapApp = $ldapApp;
+ $this->view->activeLDAP = $this->system->getActiveLDAP();
+ $this->view->ldapSettings = $this->system->getExtraLDAPSettings();
+
+ $this->display();
+ }
+
+ /**
+ * Edit extra LDAP.
+ *
+ * @access public
+ * @return void
+ */
+ public function editLDAP()
+ {
+ $channel = $this->app->session->cloudChannel ? $this->app->session->cloudChannel : $this->config->cloud->api->channel;
+ $ldapApp = $this->loadModel('store')->getAppInfoByChart('openldap', $channel, false);
+ if($_POST)
+ {
+ session_write_close();
+ $this->system->updateLDAP($ldapApp, $channel);
+ if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
+
+ $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->ldapUpdateSuccess, 'locate' => $this->inLink('ldapView')));
+ }
+
+ $this->view->title = $this->lang->system->editLDAP;
+
+
+ $this->view->ldapApp = $ldapApp;
+ $this->view->activeLDAP = $this->system->getActiveLDAP();
+ $this->view->ldapSettings = $this->system->getExtraLDAPSettings();
+ $this->display();
+ }
+
+ /**
+ * ajaxUpdatingLDAPProgress
+ *
+ * @access public
+ * @return void
+ */
+ public function ajaxUpdatingLDAPProgress()
+ {
+ session_write_close();
+
+ $number = $this->loadModel('setting')->getItem('owner=system&module=common§ion=ldap&key=updatingProgress');
+ echo sprintf($this->lang->system->LDAP->updatingProgress, intval($number));
+ }
+
+ /**
+ * LDAP view.
+ *
+ * @access public
+ * @return void
+ */
+ public function ldapView()
+ {
+ $this->loadModel('instance');
+ $this->app->loadLang('instance');
+
+ $ldapInstance = new stdclass;
+ $ldapInstance->id = 0;
+
+ $activeLDAP = $this->loadModel('setting')->getItem('owner=system&module=common§ion=ldap&key=active');
+ if($activeLDAP == 'qucheng')
+ {
+ $instanceID = $this->loadModel('setting')->getItem('owner=system&module=common§ion=ldap&key=instanceID');
+ $ldapInstance = $this->instance->getByID($instanceID);
+ if(empty($ldapInstance)) return print js::alert($this->lang->system->notices->noLDAP);
+
+ $ldapInstance = $this->instance->freshStatus($ldapInstance);
+ $ldapSettings = json_decode($ldapInstance->ldapSettings);
+ }
+ else if($activeLDAP == 'extra')
+ {
+ $ldapSettings = $this->system->getExtraLDAPSettings();
+ }
+ else
+ {
+ return print js::alert($this->lang->system->notices->noLDAP);
+ }
+
+ $this->lang->switcherMenu = $this->system->getLDAPSwitcher();
+
+ $this->view->title = $this->lang->system->ldapManagement;
+
+ $this->view->activeLDAP = $activeLDAP;
+ $this->view->instanceID = $ldapInstance->id;
+ $this->view->ldapInstance = $ldapInstance;
+ $this->view->ldapSettings = $ldapSettings;
+
+ $this->display();
+ }
+
+ /**
+ * Uninstall all LDAP in system. (This function is only for debug and test.)
+ *
+ * @access public
+ * @return void
+ */
+ public function uninstallLDAP()
+ {
+ if(!$this->config->debug) return; // Only run in debug mode.
+
+ /* 1. uninstall QuCheng LDAP. */
+ if($this->system->uninstallQuChengLDAP())
+ {
+ echo date('Y-m-d H:i:s') . ": Uninstall QuCheng LDAP success.
";
+ }
+ else
+ {
+ echo date('Y-m-d H:i:s') . ": Uninstall QuCheng LDAP fail.
";
+ $errors = dao::getError();
+ foreach($errors as $error) echo $error . '
';
+ }
+
+ /* 2. uninstall extra LDAP. */
+ if($this->system->uninstallExtraLDAP())
+ {
+ echo date('Y-m-d H:i:s') . ": Uninstall extra LDAP success.
";
+ }
+ else{
+ echo date('Y-m-d H:i:s') . ": Uninstall extra LDAP fail.
";
+ $errors = dao::getError();
+ foreach($errors as $error) echo $error . '
';
+ }
+ }
+
+ /**
+ * OSS view.
+ *
+ * @access public
+ * @return void
+ */
+ public function ossView()
+ {
+ $this->loadModel('cne');
+
+ $minioInstance = new stdclass;
+ $minioInstance->k8name = 'cne-operator';
+ $minioInstance->spaceData = new stdclass;
+ $minioInstance->spaceData->k8space = $this->config->k8space;
+
+ $ossAccount = $this->cne->getDefaultAccount($minioInstance, '', 'minio');
+ $ossDomain = $this->cne->getDomain($minioInstance, '', 'minio');
+ $this->lang->switcherMenu = $this->system->getOssSwitcher();
+
+ $this->view->title = $this->lang->system->oss->common;
+ $this->view->ossAccount = $ossAccount;
+ $this->view->ossDomain = $ossDomain;
+
+ $this->display();
+ }
+
+ /**
+ * Install SMTP.
+ *
+ * @access public
+ * @return void
+ */
+ public function installSMTP()
+ {
+ if($this->system->smtpSnippetName()) return print(js::locate($this->inLink('smtpView')));
+
+ if($_POST)
+ {
+ $channel = $this->app->session->cloudChannel ? $this->app->session->cloudChannel : $this->config->cloud->api->channel;
+ $this->system->installSysSMTP($channel);
+
+ if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
+
+ $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->smtpInstallSuccess, 'locate' => $this->inLink('smtpView')));
+ }
+
+ $this->lang->switcherMenu = $this->system->getSMTPSwitcher();
+
+ $this->view->title = $this->lang->system->SMTP->common;
+ $this->view->smtpSettings = $this->system->getSMTPSettings();
+ $this->view->smtpLinked = false;
+ $this->view->activeSMTP = false;
+
+ $this->display();
+ }
+
+ /**
+ * Edit SMTP.
+ *
+ * @access public
+ * @return void
+ */
+ public function editSMTP()
+ {
+ $this->loadModel('instance');
+ $this->app->loadLang('instance');
+
+ if($_POST)
+ {
+ $channel = $this->app->session->cloudChannel ? $this->app->session->cloudChannel : $this->config->cloud->api->channel;
+ $postData = fixer::input('post')->setDefault('source', 'qucheng')->get();
+ $this->system->updateSMTPSettings();
+
+ if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
+
+ $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->smtpUpdateSuccess, 'locate' => $this->inLink('smtpView')));
+ }
+
+ $this->lang->switcherMenu = $this->system->getSMTPSwitcher();
+
+ $this->view->title = $this->lang->system->SMTP->editSMTP;
+ $this->view->smtpSettings = $this->system->getSMTPSettings();
+ $this->view->smtpLinked = $this->instance->countSMTP();
+ $this->view->activeSMTP = zget($this->view->smtpSettings, 'enabled', false);
+
+ $this->display();
+ }
+
+ /**
+ * Show SMTP setting detail.
+ *
+ * @access public
+ * @return void
+ */
+ public function smtpView()
+ {
+ $this->loadModel('instance');
+ $this->app->loadLang('instance');
+
+ $instanceID = $this->loadModel('setting')->getItem('owner=system&module=common§ion=smtp&key=instanceID');
+ $smtpInstance = $this->instance->getByID($instanceID);
+ if(empty($smtpInstance)) return print js::alert($this->lang->system->notices->notFoundSMTPService);
+
+
+ $this->lang->switcherMenu = $this->system->getSMTPSwitcher();
+
+ $this->view->title = $this->lang->system->SMTP->common;
+ $this->view->smtpSettings = $this->system->getSMTPSettings();
+ $this->view->smtpInstance = $smtpInstance;
+
+ $this->display();
+ }
+
+ /**
+ * Uninstall SMTP. (This function is only for debug and test.)
+ *
+ * @access public
+ * @return void
+ */
+ public function uninstallSMTP()
+ {
+ if(!$this->config->debug) return; // Only run in debug mode.
+
+ /* Uninstall system SMTP proxy APP. */
+ if($this->system->uninstallSysSMTP())
+ {
+ echo date('Y-m-d H:i:s') . ": Uninstall system SMTP success.
";
+ }
+ else
+ {
+ echo date('Y-m-d H:i:s') . ": Uninstall system SMTP fail.
";
+ $errors = dao::getError();
+ foreach($errors as $error) echo $error . '
';
+ }
+ }
+
+ /**
+ * Config customer's domain.
+ *
+ * @access public
+ * @return void
+ */
+ public function configDomain()
+ {
+ $domainSettings = $this->system->getDomainSettings();
+ if($domainSettings->customDomain) $this->locate($this->inLink('domainView'));
+ $this->locate($this->inLink('editDomain'));
+ }
+
+ /**
+ * Edit customer's domain.
+ *
+ * @access public
+ * @return void
+ */
+ public function editDomain()
+ {
+ $this->loadModel('instance');
+
+ if($_POST)
+ {
+ session_write_close();
+ $this->system->saveDomainSettings();
+ if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::$errors));
+
+ return $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->updateDomainSuccess, 'locate' => $this->inlink('domainView')));
+ }
+
+ $this->view->title = $this->lang->system->domain->common;
+ $this->view->domainSettings = $this->system->getDomainSettings();
+
+ $this->display();
+ }
+
+ /**
+ * AjaxValidCert
+ *
+ * @access public
+ * @return void
+ */
+ public function ajaxValidateCert()
+ {
+ $certData = fixer::input('post')->get();
+
+ $this->dao->select('*')->from('system')->data($certData)
+ ->batchCheck('customDomain,certPem,certKey', 'notempty');
+ if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::$errors));
+
+ if(!validater::checkREG($certData->customDomain, '/^((?!-)[a-z0-9-]{1,63}(?send(array('result' => 'fail', 'message' => $this->lang->system->errors->invalidDomain));
+ }
+
+ $certName = 'tls-' . str_replace('.', '-',$certData->customDomain);
+ $result = $this->loadModel('cne')->validateCert($certName, $certData->certPem, $certData->certKey, $certData->customDomain);
+ if($result->code == 200) return $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->validCert));
+
+ return $this->send(array('result' => 'fail', 'message' => $result->message));
+ }
+
+ /**
+ * Show progress of updating domains.
+ *
+ * @access public
+ * @return void
+ */
+ public function ajaxUpdatingDomainProgress()
+ {
+ session_write_close();
+
+ $oldDomainQty = $this->loadModel('instance')->countOldDomain();
+ return print(sprintf($this->lang->system->domain->updatingProgress, $oldDomainQty));
+ }
+
+ /**
+ * Domain settings view.
+ *
+ * @access public
+ * @return void
+ */
+ public function domainView()
+ {
+ $domainSettings = $this->system->getDomainSettings();
+ $certName = 'tls-' . str_replace('.', '-', $domainSettings->customDomain);
+ $cert = $this->loadModel('cne')->certInfo($certName);
+
+ $notAfter = zget($cert, 'not_after', '');
+ if($notAfter) $cert->expiredDate = date('Y-m-d H:i:s', $notAfter);
+
+ $this->view->title = $this->lang->system->domain->common;
+ $this->view->domainSettings = $domainSettings;
+ $this->view->cert = $cert;
+
+ $this->display();
+ }
+
+ /**
+ * Config Server Load Balancing.
+ *
+ * @access public
+ * @return void
+ */
+ public function configSLB()
+ {
+ $slbSettings = $this->system->getSLBSettings();
+ if($slbSettings->name) return print(js::locate($this->inLink('slbView')));
+
+ return print(js::locate($this->inLink('editSLB')));
+ }
+
+ /**
+ * Edit SLB.
+ *
+ * @access public
+ * @return void
+ */
+ public function editSLB()
+ {
+ if($_POST)
+ {
+ $this->system->saveSLBSettings();
+ if(dao::isError())
+ {
+ return $this->send(array('result' => 'fail', 'message' => dao::getError()));
+ }
+
+ return $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->configSLBSuccess, 'locate' => $this->inLink('SLBView')));
+ }
+
+ $this->view->title = $this->lang->system->SLB->common;
+ $this->view->SLBSettings = $this->system->getSLBSettings();
+
+ $this->display();
+ }
+
+ /**
+ * SLB View.
+ *
+ * @access public
+ * @return void
+ */
+ public function SLBView()
+ {
+ $this->view->title = $this->lang->system->SLB->common;
+ $this->view->slbSettings = $this->system->getSLBSettings();
+
+ $this->display();
+ }
+
+ public function browseBackup()
+ {
+ $backups = array();
+ if(empty($this->view->error))
+ {
+ $sqlFiles = glob("{$this->backupPath}*.sql*");
+ if(!empty($sqlFiles))
+ {
+ foreach($sqlFiles as $file)
+ {
+ $fileName = basename($file);
+ $backupFile = new stdclass();
+ $backupFile->time = filemtime($file);
+ $backupFile->name = substr($fileName, 0, strpos($fileName, '.'));
+ $backupFile->sqlSummary = $this->system->getSQLSummary($file);
+ $backupFile->files[$file] = $this->backup->getBackupSummary($file);
+
+ $fileBackup = $this->backup->getBackupFile($backupFile->name, 'file');
+ if($fileBackup) $backupFile->files[$fileBackup] = $this->backup->getBackupSummary($fileBackup);
+
+ $codeBackup = $this->backup->getBackupFile($backupFile->name, 'code');
+ if($codeBackup) $backupFile->files[$codeBackup] = $this->backup->getBackupSummary($codeBackup);
+
+ $backups[$backupFile->name] = $backupFile;
+ }
+ }
+ }
+ krsort($backups);
+
+ $this->view->title = $this->lang->backup->common;
+ $this->view->position[] = $this->lang->backup->common;
+ $this->view->backups = $backups;
+ $this->display();
+ }
+
+ /**
+ * Verify SMTP account by ajax.
+ *
+ * @access public
+ * @return void
+ */
+ public function ajaxVerifySMTPAccount()
+ {
+ $accountInfo= fixer::input('post')->get();
+
+ $passed = $this->loadModel('cne')->validateSMTP($accountInfo);
+ if($passed) $this->send(array('result' => 'success', 'message' => $this->lang->system->notices->verifySMTPSuccess));
+
+ $this->send(array('result' => 'fail', 'message' => $this->lang->system->errors->verifySMTPFailed));
+ }
+
+ /**
+ * Generate database auth parameters and jump to login page.
+ *
+ * @access public
+ * @return void
+ */
+ public function ajaxDBAuthUrl()
+ {
+ $post = fixer::input('post')
+ ->setDefault('namespace', 'default')
+ ->get();
+ if(empty($post->dbName)) return $this->send(array('result' => 'fail', 'message' => $this->lang->system->errors->dbNameIsEmpty));
+
+ $detail = $this->loadModel('cne')->dbDetail($post->dbName, $post->namespace);
+ if(empty($detail)) return $this->send(array('result' => 'fail', 'message' => $this->lang->system->errors->notFoundDB));
+
+ $this->app->loadConfig('instance');
+ $dbAuth = array();
+ $dbAuth['driver'] = zget($this->config->instance->adminer->dbTypes, $post->dbType, '');
+ $dbAuth['server'] = $detail->host . ':' . $detail->port;
+ $dbAuth['username'] = $detail->username;
+ $dbAuth['db'] = $detail->database;
+ $dbAuth['password'] = $detail->password;
+
+ $url = '/adminer?'. http_build_query($dbAuth);
+ $this->send(array('result' => 'success', 'message' => '', 'data' => array('url' => $url)));
+ }
+
+ /**
+ * Get LDAP info by ajax.
+ *
+ * @access public
+ * @return void
+ */
+ public function ajaxLdapInfo()
+ {
+ $instanceID = $this->loadModel('setting')->getItem('owner=system&module=common§ion=ldap&key=instanceID');
+ $ldapInstance = $this->loadModel('instance')->getByID($instanceID);
+ if(empty($ldapInstance)) return $this->send(array('result' => 'fail', 'message' => $this->lang->system->errors->notFoundLDAP));
+
+ $ldapSetting = json_decode($ldapInstance->ldapSettings);
+
+ $secretKey = helper::readKey();
+ $password = openssl_decrypt($ldapSetting->auth->password, 'DES-ECB', $secretKey);
+ if(!$password) $password = openssl_decrypt($ldapSetting->auth->password, 'DES-ECB', $ldapInstance->createdAt); // Secret key was createdAt field value less v2.2.
+
+ $this->send(array('result' => 'success', 'message' => '', 'data' => array('domain' => $this->instance->url($ldapInstance), 'account' => $ldapSetting->auth->username, 'pass' => $password)));
+ }
+
+ /**
+ * Get oss account and domain by ajax.
+ *
+ * @access public
+ * @return void
+ */
+ public function ajaxOssInfo()
+ {
+ $minioInstance = new stdclass;
+ $minioInstance->k8name = 'cne-operator';
+ $minioInstance->spaceData = new stdclass;
+ $minioInstance->spaceData->k8space = $this->config->k8space;
+
+ $ossAccount = $this->loadModel('cne')->getDefaultAccount($minioInstance, '', 'minio');
+
+ $ossDomain = $this->cne->getDomain($minioInstance, '', 'minio');
+ $ossDomain->domain = $ossDomain->access_host;
+
+ $url = $this->loadModel('instance')->url($ossDomain);
+
+ if($ossAccount and $ossDomain) return $this->send(array('result' => 'success', 'message' => '', 'data' => array('account' => $ossAccount, 'url' => $url)));
+
+ $this->send(array('result' => 'fail', 'message' => $this->lang->system->errors->failGetOssAccount));
+ }
+}
diff --git a/module/system/css/dblist.css b/module/system/css/dblist.css
new file mode 100644
index 0000000000..f6f3b660e3
--- /dev/null
+++ b/module/system/css/dblist.css
@@ -0,0 +1,3 @@
+#mainContent .table {margin: auto;width: 60%; min-width:400px; max-width: 800px;}
+#mainContent .main-header {margin-bottom: 20px;}
+#mainContent .main-header h2{font-size: 18px;}
diff --git a/module/system/css/editdomain.css b/module/system/css/editdomain.css
new file mode 100644
index 0000000000..1f7c85f630
--- /dev/null
+++ b/module/system/css/editdomain.css
@@ -0,0 +1 @@
+table tr th.new-domain-label{vertical-align: top; padding-top: 14px;}
diff --git a/module/system/css/editldap.css b/module/system/css/editldap.css
new file mode 100644
index 0000000000..d1fa0d1d8b
--- /dev/null
+++ b/module/system/css/editldap.css
@@ -0,0 +1 @@
+.advanced {padding:5px 0 5px 150px;}
diff --git a/module/system/css/index.css b/module/system/css/index.css
new file mode 100644
index 0000000000..bb3b7523c8
--- /dev/null
+++ b/module/system/css/index.css
@@ -0,0 +1,7 @@
+.plug-container > div{margin-bottom: 20px;}
+.plug-container a {display: inline-block; width: 150px;}
+.plug-container a:hover { text-decoration: none; box-shadow: 0 1px 5px rgb(0 0 0 / 18%);}
+.logo { display: inline-block; padding: 10px;}
+.logo .icon {font-size: 100px; color: #70c0fb;}
+.logo img {width: 100px; height:100px;}
+.plug-title {display: inline-block; padding-top: 10px; text-align: center; font-size: 16px; font-weight: 500;}
diff --git a/module/system/css/installldap.css b/module/system/css/installldap.css
new file mode 100644
index 0000000000..d1fa0d1d8b
--- /dev/null
+++ b/module/system/css/installldap.css
@@ -0,0 +1 @@
+.advanced {padding:5px 0 5px 150px;}
diff --git a/module/system/css/installsmtp.css b/module/system/css/installsmtp.css
new file mode 100644
index 0000000000..dde26ea91f
--- /dev/null
+++ b/module/system/css/installsmtp.css
@@ -0,0 +1 @@
+#mainContent .main-col {display: block;}
diff --git a/module/system/css/ldapview.css b/module/system/css/ldapview.css
new file mode 100644
index 0000000000..b449e569c2
--- /dev/null
+++ b/module/system/css/ldapview.css
@@ -0,0 +1,7 @@
+.btn.label-lg.label-primary.btn-edit {color: #61be68;}
+.btn.label-lg.label-outline {background-color: #ffffff!important;}
+.btn.label-lg.label-outline:hover, .btn-visit.btn:focus {background-color: #e5fbf3!important;}
+.edit-tools-tips {display: inline-block; margin: 0 10px;}
+#ldapAccountModal .modal-content {padding-bottom: 20px;}
+input#ldapPassword {border:none;}
+#ldapPassBtn {border:none;}
diff --git a/module/system/css/ossview.css b/module/system/css/ossview.css
new file mode 100644
index 0000000000..5c80d0a5d5
--- /dev/null
+++ b/module/system/css/ossview.css
@@ -0,0 +1,5 @@
+.btn.label-lg.label-outline {background-color: #ffffff!important;}
+.btn.label-lg.label-outline:hover, .btn-visit.btn:focus {background-color: #e5fbf3!important;}
+input#ossPassword {border:none;}
+input#ossSK{border:none;}
+
diff --git a/module/system/css/smtpview.css b/module/system/css/smtpview.css
new file mode 100644
index 0000000000..899c01945b
--- /dev/null
+++ b/module/system/css/smtpview.css
@@ -0,0 +1 @@
+#smtpPassBtn{border: 0;}
diff --git a/module/system/js/dblist.js b/module/system/js/dblist.js
new file mode 100644
index 0000000000..3b5f658b31
--- /dev/null
+++ b/module/system/js/dblist.js
@@ -0,0 +1,22 @@
+$(function()
+{
+ $('button.db-login').on('click', function(event)
+ {
+ var dbName = $(event.target).data('db-name');
+ var dbType = $(event.target).data('db-type');
+ var namespace = $(event.target).data('namespace');
+
+ $.post(createLink('system', 'ajaxDBAuthUrl'), {dbName, namespace, dbType}).done(function(res)
+ {
+ var response = JSON.parse(res);
+ if(response.result == 'success')
+ {
+ window.parent.open(response.data.url, 'Adminer');
+ }
+ else
+ {
+ bootbox.alert(response.message);
+ }
+ });
+ });
+});
diff --git a/module/system/js/dblist.ui.js b/module/system/js/dblist.ui.js
new file mode 100644
index 0000000000..6039bf2e9a
--- /dev/null
+++ b/module/system/js/dblist.ui.js
@@ -0,0 +1,27 @@
+window.manageDb = function(dbName, dbType, namespace)
+{
+ $.post($.createLink('system', 'ajaxDBAuthUrl'), {dbName, namespace, dbType}, function(res)
+ {
+ var response = JSON.parse(res);
+ if(response.result == 'success')
+ {
+ window.parent.open(response.data.url, 'Adminer');
+ }
+ else
+ {
+ zui.Modal.alert(response.message);
+ }
+ });
+}
+
+window.renderDbList = function (result, {col, row, value})
+{
+ if(col.name === 'status')
+ {
+ var statusClass = value == 'running' ? 'text-success' : '';
+ result[0] = {html: '
' + result[0] + ''};
+ return result;
+ }
+
+ return result;
+}
diff --git a/module/system/js/editdomain.js b/module/system/js/editdomain.js
new file mode 100644
index 0000000000..39651e41bc
--- /dev/null
+++ b/module/system/js/editdomain.js
@@ -0,0 +1,143 @@
+$(function()
+{
+ /**
+ * Fresh submitBtn status.
+ *
+ * @access public
+ * @return void
+ */
+ function freshSubmitBtn()
+ {
+ if($('#httpstrue:checked').length == 0)
+ {
+ $('#submitBtn').attr('disabled', false);
+ return;
+ }
+ var pass = $('#validateCertBtn').attr('pass') == 'true';
+ if(pass)
+ {
+ $('#submitBtn').attr('disabled', false);
+ }
+ else
+ {
+ $('#submitBtn').attr('disabled', true);
+ }
+ }
+
+ freshSubmitBtn();
+
+ /**
+ * Toggle certificate textarea.
+ *
+ * @access public
+ * @return void
+ */
+ function toggleCertBox()
+ {
+ $showCert = $("#httpstrue[type=checkbox]:checked").length > 0;
+ if($showCert)
+ {
+ $('#cert-box').show();
+ }
+ else
+ {
+ $('#cert-box').hide();
+ }
+
+ freshSubmitBtn();
+ }
+
+ toggleCertBox();
+
+ $("#httpstrue[type=checkbox]").on('change', function()
+ {
+ toggleCertBox();
+ });
+
+ $('#validateCertBtn').on('click', function()
+ {
+ var certData = {};
+ certData.customDomain = $('#customDomain').val();
+ certData.certPem = $('#certPem').val();
+ certData.certKey = $('#certKey').val();
+ $.post(createLink('system', 'ajaxValidateCert'), certData).done(function(response)
+ {
+ var res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ $('#validateCertBtn').attr('pass', 'true');
+ $('#validateMsg').removeClass('text-red').addClass('text-green');
+ $('#validateMsg').html(res.message);
+ }
+ else
+ {
+ $('#validateCertBtn').attr('pass', 'false');
+ $('#validateMsg').removeClass('text-green').addClass('text-red');
+ var errMessage = res.message;
+ if(res.message instanceof Array) errMessage = res.message.join(' ');
+ if(res.message instanceof Object) errMessage = Object.values(res.message).join(' ');
+
+ $('#validateMsg').html(errMessage);
+ }
+ freshSubmitBtn();
+ });
+ });
+
+ var timerID = 0;
+
+ /**
+ * Show progress modal and fresh progress of updating domain by ajax.
+ *
+ * @access public
+ * @return void
+ */
+ function showProgressModal()
+ {
+ $('#waiting').modal('show');
+ timerID = setInterval(function()
+ {
+ $.get(createLink('system', 'ajaxUpdatingDomainProgress'), function(data)
+ {
+ $('#waiting #message').html(data);
+ });
+ }, 1000);
+ };
+
+ $('#submitBtn').on('click', function()
+ {
+ bootbox.confirm(notices.confirmUpdateDomain, function(result)
+ {
+ if(!result) return;
+
+ showProgressModal();
+ $('#submitBtn').attr('disabled', true);
+
+ var domainData = $('#domainForm').serializeArray();
+ $.post(createLink('system', 'editDomain'), domainData).done(function(response)
+ {
+ $('#submitBtn').attr('disabled', false);
+
+ var res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ parent.window.location.href = res.locate;
+ }
+ else
+ {
+ $('#waiting').modal('hide');
+ clearInterval(timerID);
+
+ var errMessage = res.message;
+ if(res.message instanceof Array) errMessage = res.message.join('
');
+ if(res.message instanceof Object) errMessage = Object.values(res.message).join('
');
+
+ bootbox.alert(
+ {
+ title: notices.fail,
+ message: errMessage,
+ });
+ }
+ });
+ });
+ });
+});
diff --git a/module/system/js/editdomain.ui.js b/module/system/js/editdomain.ui.js
new file mode 100644
index 0000000000..85d9c76f89
--- /dev/null
+++ b/module/system/js/editdomain.ui.js
@@ -0,0 +1,52 @@
+function onHttpsChange(event)
+{
+ const $https = $(event.target);
+
+ if($https.prop('checked'))
+ {
+ $('.cert').removeClass('hidden');
+
+ var pass = $('#validateCertBtn').attr('pass') == 'true';
+ if(!pass) $('#domainForm button[type=submit]').prop('disabled', true);
+ }
+ else
+ {
+ $('.cert').addClass('hidden');
+ $('#domainForm button[type=submit]').prop('disabled', false);
+ }
+}
+
+function checkCert()
+{
+ var certData = {};
+ certData.customDomain = $('#customDomain').val();
+ certData.certPem = $('#certPem').val();
+ certData.certKey = $('#certKey').val();
+
+ $.ajaxSubmit(
+ {
+ url: $.createLink('system', 'ajaxValidateCert'),
+ data: certData,
+ onComplete: function(res)
+ {
+ if(res.result == 'success')
+ {
+ $('#validateCertBtn').attr('pass', 'true');
+ $('#validateMsg').removeClass('text-danger').addClass('text-success');
+ $('#validateMsg').html(res.message);
+ $('#domainForm button[type=submit]').prop('disabled', false);
+ }
+ else
+ {
+ $('#validateCertBtn').attr('pass', 'false');
+ $('#validateMsg').removeClass('text-success').addClass('text-danger');
+ var errMessage = res.message;
+ if(res.message instanceof Array) errMessage = res.message.join(' ');
+ if(res.message instanceof Object) errMessage = Object.values(res.message).join(' ');
+
+ $('#validateMsg').html(errMessage);
+ }
+ },
+ });
+
+}
diff --git a/module/system/js/editldap.js b/module/system/js/editldap.js
new file mode 100644
index 0000000000..583bd742b6
--- /dev/null
+++ b/module/system/js/editldap.js
@@ -0,0 +1,155 @@
+$(function()
+{
+ /**
+ * Fresh submit button.
+ *
+ * @access public
+ * @return void
+ */
+ function freshSubmitBtn()
+ {
+ var enableLDAP = $('#LDAPForm input[type=checkbox]:checked').length > 0;
+ var ldapCheckPass = true;
+ if($('#LDAPForm select[name=source]').val() == 'extra')
+ {
+ ldapCheckPass = $('#testConnectBtn').attr('pass') == 'true';
+ }
+
+ if(enableLDAP && ldapCheckPass)
+ {
+ $('#LDAPForm #submitBtn').attr('disabled', false);
+ }
+ else
+ {
+ $('#LDAPForm #submitBtn').attr('disabled', true);
+ }
+ }
+
+ freshSubmitBtn();
+
+ $('#LDAPForm input[type=checkbox]').on('change', function(event)
+ {
+ freshSubmitBtn();
+ });
+
+ $('#LDAPForm input[type=checkbox]').change();
+
+ $('select[name=source]').on('change', function(event)
+ {
+ if($(event.target).val() == 'qucheng')
+ {
+ $('#quchengLDAP').show();
+ $('#extraLDAP').hide();
+ }
+ else
+ {
+ $('#quchengLDAP').hide();
+ $('#extraLDAP').show();
+ }
+
+ freshSubmitBtn();
+ });
+ $('select[name=source]').change();
+
+ $('#testConnectBtn').on('click', function(event)
+ {
+ var settings = {};
+ settings.host = $('input[name="extra[host]"]').val();
+ settings.port = $('input[name="extra[port]"]').val();
+ settings.bindDN = $('input[name="extra[bindDN]"]').val();
+ settings.bindPass = $('input[name="extra[bindPass]"]').val();
+ settings.baseDN = $('input[name="extra[baseDN]"]').val();
+ if(!settings.host || !settings.port || !settings.bindDN || !settings.bindPass || !settings.baseDN)
+ {
+ bootbox.alert(
+ {
+ title: notices.attention,
+ message: notices.fillAllRequiredFields,
+ });
+ return;
+ }
+
+ $.post(createLink('system', 'testLDAPConnection'), settings).done(function(response)
+ {
+ try
+ {
+ var res = JSON.parse(response);
+ }
+ catch(error)
+ {
+ var res = {result: 'fail', message: errors.verifyLDAPFailed,};
+ }
+ $('#connectResult').html(res.message);
+ if(res.result == 'success')
+ {
+ $('#testConnectBtn').attr('pass', 'true');
+ $('#connectResult').removeClass('text-red').addClass('text-success');
+ freshSubmitBtn();
+ }
+ else
+ {
+ $('#testConnectBtn').attr('pass', 'false');
+ $('#connectResult').removeClass('text-success').addClass('text-red');
+ freshSubmitBtn();
+ }
+ });
+ });
+
+ var timerID = 0;
+
+ /**
+ * Show updating LDAP progress modal.
+ *
+ * @access public
+ * @return void
+ */
+ function showProgressModal()
+ {
+ $('#waiting').modal('show');
+ timerID = setInterval(function()
+ {
+ $.get(createLink('system', 'ajaxUpdatingLDAPProgress'), function(data)
+ {
+ $('#waiting #message').html(data);
+ });
+ }, 1000);
+ };
+
+ $('#submitBtn').on('click', function()
+ {
+ bootbox.confirm(notices.confirmUpdateLDAP, function(result)
+ {
+ if(!result) return;
+
+ showProgressModal();
+ $('#submitBtn').attr('disabled', true);
+
+ var ldapData = $('#LDAPForm').serializeArray();
+ $.post(createLink('system', 'editLDAP'), ldapData).done(function(response)
+ {
+ $('#submitBtn').attr('disabled', false);
+
+ var res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ parent.window.location.href = res.locate;
+ }
+ else
+ {
+ $('#waiting').modal('hide');
+ clearInterval(timerID);
+
+ var errMessage = res.message;
+ if(res.message instanceof Array) errMessage = res.message.join('
');
+ if(res.message instanceof Object) errMessage = Object.values(res.message).join('
');
+
+ bootbox.alert(
+ {
+ title: notices.fail,
+ message: errMessage,
+ });
+ }
+ });
+ });
+ });
+});
diff --git a/module/system/js/editslb.js b/module/system/js/editslb.js
new file mode 100644
index 0000000000..c1e98ad428
--- /dev/null
+++ b/module/system/js/editslb.js
@@ -0,0 +1,34 @@
+$(function()
+{
+ var overtime = 30;
+ var timerID = 0;
+ $("#submitBtn").on('click', function()
+ {
+ $('#waiting').modal('show');
+ timerID = setInterval(function()
+ {
+ $('#timer').text(overtime);
+ overtime--;
+ }, 1000);
+
+ var slbData = {};
+ slbData.ippool = $('#ippool').val();
+ $.post(createLink('system', 'editSLB'), slbData).done(function(response)
+ {
+ $('#waiting').modal('hide');
+ clearInterval(timerID);
+ overtime = 30;
+
+ var res = JSON.parse(response);
+ if(res.result == 'success'){
+ parent.window.location.href = res.locate;
+ }else{
+ bootbox.alert(
+ {
+ title: notices.fail,
+ message: res.message,
+ });
+ }
+ });
+ });
+});
diff --git a/module/system/js/editsmtp.js b/module/system/js/editsmtp.js
new file mode 100644
index 0000000000..664f09ff5a
--- /dev/null
+++ b/module/system/js/editsmtp.js
@@ -0,0 +1,73 @@
+$(function()
+{
+ /**
+ * Fresh submit btton by enable SMTP checkboc value and verified result of SMTP account.
+ *
+ * @access public
+ * @return void
+ */
+ function freshSubmitBtn()
+ {
+ var enableSMTP = $('#smtpForm input[type=checkbox]:checked').length > 0;
+ var accountRight = $('#verifyAccountBtn').attr('pass') == 'true';
+
+ if(enableSMTP && accountRight)
+ {
+ $('#smtpForm button[type=submit]').attr('disabled', false);
+ }
+ else
+ {
+ $('#smtpForm button[type=submit]').attr('disabled', true);
+ }
+ }
+
+ $('#smtpForm input[type=checkbox]').on('change', function(event)
+ {
+ freshSubmitBtn();
+ });
+
+ $('#smtpForm input[type=checkbox]').change();
+
+ $('#verifyAccountBtn').on('click', function(event)
+ {
+ var settings = {};
+ settings.host = $("input[name='host']").val();
+ settings.port = $("input[name='port']").val();
+ settings.user = $("input[name='user']").val();
+ settings.pass = $("input[name='pass']").val();
+ if(!settings.host || !settings.port || !settings.user || !settings.pass)
+ {
+ bootbox.alert(
+ {
+ title: notices.attention,
+ message: notices.fillAllRequiredFields,
+ });
+ return;
+ }
+
+ $.post(createLink('system', 'ajaxVerifySMTPAccount'), settings).done(function(response)
+ {
+ try
+ {
+ var res = JSON.parse(response);
+ }
+ catch(error)
+ {
+ var res = {result: 'fail', message: errors.verifySMTPFailed,};
+ }
+ $('#verifyResult').html(res.message);
+ if(res.result == 'success')
+ {
+ $('#verifyAccountBtn').attr('pass', 'true');
+ $('#verifyResult').removeClass('text-red').addClass('text-success');
+ freshSubmitBtn();
+ }
+ else
+ {
+ $('#verifyAccountBtn').attr('pass', 'false');
+ $('#verifyResult').removeClass('text-success').addClass('text-red');
+ freshSubmitBtn();
+ }
+ });
+ });
+});
diff --git a/module/system/js/installldap.js b/module/system/js/installldap.js
new file mode 100644
index 0000000000..fe5297b8e1
--- /dev/null
+++ b/module/system/js/installldap.js
@@ -0,0 +1,129 @@
+$(function()
+{
+ /**
+ * Fresh submit button.
+ *
+ * @access public
+ * @return void
+ */
+ function freshSubmitBtn()
+ {
+ var enableLDAP = $('#LDAPForm input[type=checkbox]:checked').length > 0;
+ var ldapCheckPass = true;
+ if($('#LDAPForm select[name=source]').val() == 'extra')
+ {
+ ldapCheckPass = $('#testConnectBtn').attr('pass') == 'true';
+ }
+
+ if(enableLDAP && ldapCheckPass)
+ {
+ $('#LDAPForm #submitBtn').attr('disabled', false);
+ }
+ else
+ {
+ $('#LDAPForm #submitBtn').attr('disabled', true);
+ }
+ }
+
+ freshSubmitBtn();
+
+ $('#LDAPForm input[type=checkbox]').on('change', function(event)
+ {
+ freshSubmitBtn();
+ });
+
+ $('#LDAPForm input[type=checkbox]').change();
+
+ $('select[name=source]').on('change', function(event)
+ {
+ if($(event.target).val() == 'qucheng')
+ {
+ $('#quchengLDAP').show();
+ $('#extraLDAP').hide();
+ }
+ else
+ {
+ $('#quchengLDAP').hide();
+ $('#extraLDAP').show();
+ }
+
+ freshSubmitBtn();
+ });
+ $('select[name=source]').change();
+
+ $('#testConnectBtn').on('click', function(event)
+ {
+ var settings = {};
+ settings.host = $('input[name="extra[host]"]').val();
+ settings.port = $('input[name="extra[port]"]').val();
+ settings.bindDN = $('input[name="extra[bindDN]"]').val();
+ settings.bindPass = $('input[name="extra[bindPass]"]').val();
+ settings.baseDN = $('input[name="extra[baseDN]"]').val();
+ if(!settings.host || !settings.port || !settings.bindDN || !settings.bindPass || !settings.baseDN)
+ {
+ bootbox.alert(
+ {
+ title: notices.attention,
+ message: notices.fillAllRequiredFields,
+ });
+ return;
+ }
+
+ $.post(createLink('system', 'testLDAPConnection'), settings).done(function(response)
+ {
+ try
+ {
+ var res = JSON.parse(response);
+ }
+ catch(error)
+ {
+ var res = {result: 'fail', message: errors.verifyLDAPFailed,};
+ }
+ $('#connectResult').html(res.message);
+ if(res.result == 'success')
+ {
+ $('#testConnectBtn').attr('pass', 'true');
+ $('#connectResult').removeClass('text-red').addClass('text-success');
+ freshSubmitBtn();
+ }
+ else
+ {
+ $('#testConnectBtn').attr('pass', 'false');
+ $('#connectResult').removeClass('text-success').addClass('text-red');
+ freshSubmitBtn();
+ }
+ });
+ });
+
+ $('#submitBtn').on('click', function()
+ {
+ $('#submitBtn').attr('disabled', true);
+
+ var ldapData = $('#LDAPForm').serializeArray();
+ $.post(createLink('system', 'installLDAP'), ldapData).done(function(response)
+ {
+ $('#submitBtn').attr('disabled', false);
+
+ var res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ parent.window.location.href = res.locate;
+ }
+ else
+ {
+ $('#waiting').modal('hide');
+ clearInterval(timerID);
+
+ var errMessage = res.message;
+ if(res.message instanceof Array) errMessage = res.message.join('
');
+ if(res.message instanceof Object) errMessage = Object.values(res.message).join('
');
+
+ bootbox.alert(
+ {
+ title: notices.fail,
+ message: errMessage,
+ });
+ }
+ });
+ });
+});
diff --git a/module/system/js/installsmtp.js b/module/system/js/installsmtp.js
new file mode 100644
index 0000000000..a18e743352
--- /dev/null
+++ b/module/system/js/installsmtp.js
@@ -0,0 +1,91 @@
+$(function()
+{
+ /**
+ * Fresh submit btton by enable SMTP checkboc value and verified result of SMTP account.
+ *
+ * @access public
+ * @return void
+ */
+ function freshSubmitBtn()
+ {
+ var enableSMTP = $('#smtpForm input[type=checkbox]:checked').length > 0;
+ var accountRight = $('#verifyAccountBtn').attr('pass') == 'true';
+
+ if(enableSMTP)
+ {
+ $('#smtpForm').find('table').show();
+ $('#smtpForm').find('button[type=submit]').show();
+ }
+ else
+ {
+ $('#smtpForm').find('table').hide();
+ $('#smtpForm').find('button[type=submit]').hide();
+ }
+
+ if(enableSMTP && accountRight)
+ {
+ $('#smtpForm button[type=submit]').attr('disabled', false);
+ }
+ else
+ {
+ $('#smtpForm button[type=submit]').attr('disabled', true);
+ }
+ }
+
+ $('#smtpForm input[type=checkbox]').on('change', function(event)
+ {
+ freshSubmitBtn();
+ });
+
+ $('#smtpForm input[type=checkbox]').change();
+
+ $('#smtpForm input[type=text]').on('change', function(event)
+ {
+ $('#verifyAccountBtn').attr('pass', 'false');
+ $('#verifyResult').text('');
+ freshSubmitBtn();
+ });
+
+ $('#verifyAccountBtn').on('click', function(event)
+ {
+ var settings = {};
+ settings.host = $("input[name='host']").val();
+ settings.port = $("input[name='port']").val();
+ settings.user = $("input[name='user']").val();
+ settings.pass = $("input[name='pass']").val();
+ if(!settings.host || !settings.port || !settings.user || !settings.pass)
+ {
+ bootbox.alert(
+ {
+ title: notices.attention,
+ message: notices.fillAllRequiredFields,
+ });
+ return;
+ }
+
+ $.post(createLink('system', 'ajaxVerifySMTPAccount'), settings).done(function(response)
+ {
+ try
+ {
+ var res = JSON.parse(response);
+ }
+ catch(error)
+ {
+ var res = {result: 'fail', message: errors.verifySMTPFailed,};
+ }
+ $('#verifyResult').html(res.message);
+ if(res.result == 'success')
+ {
+ $('#verifyAccountBtn').attr('pass', 'true');
+ $('#verifyResult').removeClass('text-red').addClass('text-success');
+ freshSubmitBtn();
+ }
+ else
+ {
+ $('#verifyAccountBtn').attr('pass', 'false');
+ $('#verifyResult').removeClass('text-success').addClass('text-red');
+ freshSubmitBtn();
+ }
+ });
+ });
+});
diff --git a/module/system/js/ldapview.js b/module/system/js/ldapview.js
new file mode 100644
index 0000000000..113ad5f7db
--- /dev/null
+++ b/module/system/js/ldapview.js
@@ -0,0 +1,127 @@
+$(function()
+{
+ $('.btn-visit').on('click', function(event)
+ {
+ $.get(createLink('system', 'ajaxLdapInfo'), function(response)
+ {
+ let res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ $('#ldapAccount').text(res.data.account);
+ $('#ldapPassword').val(res.data.pass);
+ $('#ldapVisitUrl').attr('href', '//' + res.data.domain);
+ $('#ldapAccountModal').modal('show');
+ }
+ });
+ });
+
+ $('#ldapPassBtn').on('click', function(event){
+ var inputTyep = $('#ldap_password').attr('type');
+ if(inputTyep == 'text'){
+ $('#ldap_password').attr('type', 'password');
+ $('#ldapPassBtn').find('.icon').removeClass('icon-eye').addClass('icon-eye-off');
+ }
+ else
+ {
+ $('#ldap_password').attr('type', 'text');
+ $('#ldapPassBtn').find('.icon').addClass('icon-eye').removeClass('icon-eye-off');
+ }
+ });
+
+ $('#copyPassBtn').on('click', function(event)
+ {
+ $('#ldapPassword').select();
+ document.execCommand('copy');
+ bootbox.alert(copySuccess);
+ });
+
+ $('.btn-start').on('click', function(event)
+ {
+ bootbox.confirm(instanceNotices.confirmStart, function(result)
+ {
+ if(!result) return;
+
+ var loadingDialog = bootbox.dialog(
+ {
+ message: '
' + instanceNotices.starting + '
',
+ });
+
+ let id = $(event.target).closest('button').attr('instance-id');
+ let url = createLink('instance', 'ajaxStart', 'id=' + id, 'json');
+ $.post(url).done(function(response)
+ {
+ loadingDialog.modal('hide');
+
+ let res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ window.location.reload();
+ }
+ else
+ {
+ bootbox.alert(
+ {
+ title: instanceNotices.fail,
+ message: res.message,
+ });
+ }
+ });
+ });
+ });
+
+ $('.btn-stop').on('click', function(event)
+ {
+ bootbox.confirm(instanceNotices.confirmStop, function(result)
+ {
+ if(!result) return;
+
+ var loadingDialog = bootbox.dialog(
+ {
+ message: '
' + instanceNotices.stopping + '
',
+ });
+
+ let id = $(event.target).closest('button').attr('instance-id');
+ let url = createLink('instance', 'ajaxStop', 'id=' + id, 'json');
+ $.post(url).done(function(response)
+ {
+ loadingDialog.modal('hide');
+
+ let res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ window.location.reload();
+ }
+ else
+ {
+ bootbox.alert(
+ {
+ title: instanceNotices.fail,
+ message: res.message,
+ });
+ }
+ });
+ });
+ });
+
+ setInterval(function()
+ {
+ var mainMenu = parent.window.$.apps.getLastApp();
+ if(mainMenu.code != 'system') return;
+
+ var statusURL = createLink('instance', 'ajaxStatus');
+ $.post(statusURL, {idList: instanceIdList}).done(function(response)
+ {
+ let res = JSON.parse(response);
+ if(res.result == 'success' && res.data instanceof Array)
+ {
+ res.data.forEach(function(instance)
+ {
+ if($(".instance-status[instance-id=" + instance.id + "]").data('status') != instance.status) window.location.reload();
+ });
+ }
+ if(res.locate) window.parent.location.href = res.locate;
+ });
+ }, 1000 * 5);
+
+ $('[data-toggle="tooltip"]').tooltip();
+});
diff --git a/module/system/js/ossview.js b/module/system/js/ossview.js
new file mode 100644
index 0000000000..66c388bc8c
--- /dev/null
+++ b/module/system/js/ossview.js
@@ -0,0 +1,33 @@
+$(function()
+{
+ $('#ossManage').on('click', function(event)
+ {
+ $.get(createLink('system', 'ajaxOssInfo'), function(response)
+ {
+ let res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ $('#ossAdmin').text(res.data.account.username);
+ $('#ossPassword').val(res.data.account.password);
+ $('#ossVisitUrl').attr('href', res.data.url);
+ $('#ossAccountModal').modal('show');
+ }
+ });
+ });
+
+ $('#copySKBtn').on('click', function(event)
+ {
+ $('#ossSK').removeClass('hidden').select();
+ document.execCommand('copy');
+ $('#ossSK').addClass('hidden');
+ bootbox.alert(copySuccess);
+ });
+
+ $('#copyPassBtn').on('click', function(event)
+ {
+ $('#ossPassword').removeClass('hidden').select();
+ document.execCommand('copy');
+ $('#ossPassword').addClass('hidden');
+ bootbox.alert(copySuccess);
+ });
+});
diff --git a/module/system/js/ossview.ui.js b/module/system/js/ossview.ui.js
new file mode 100644
index 0000000000..2010847a0f
--- /dev/null
+++ b/module/system/js/ossview.ui.js
@@ -0,0 +1,34 @@
+$(function()
+{
+ $.get($.createLink('system', 'ajaxOssInfo'), function(response)
+ {
+ let res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ $('#ossAdmin').text(res.data.account.username);
+ $('#ossPassword').val(res.data.account.password);
+ $('#ossVisitUrl').attr('href', res.data.url);
+ $('#ossAccountModal').modal('show');
+ }
+ });
+});
+
+function copySK()
+{
+ var sk = document.getElementById('ossSK');
+ $('#ossSK').removeClass('hidden');
+ sk.select();
+ document.execCommand('copy');
+ $('#ossSK').addClass('hidden');
+ zui.Modal.alert(copySuccess);
+}
+
+function copyPassBtn()
+{
+ var ossPassword = document.getElementById('ossPassword');
+ $('#ossPassword').removeClass('hidden');
+ ossPassword.select();
+ document.execCommand('copy');
+ $('#ossPassword').addClass('hidden');
+ zui.Modal.alert(copySuccess);
+}
diff --git a/module/system/js/smtpview.js b/module/system/js/smtpview.js
new file mode 100644
index 0000000000..3b9eb96010
--- /dev/null
+++ b/module/system/js/smtpview.js
@@ -0,0 +1,110 @@
+$(function()
+{
+ $('#smtpPassBtn').on('click', function(event){
+ var inputTyep = $('#smtp_password').attr('type');
+ if(inputTyep == 'text'){
+ $('#smtp_password').attr('type', 'password');
+ $('#smtpPassBtn').find('.icon').removeClass('icon-eye').addClass('icon-eye-off');
+ }
+ else
+ {
+ $('#smtp_password').attr('type', 'text');
+ $('#smtpPassBtn').find('.icon').addClass('icon-eye').removeClass('icon-eye-off');
+ }
+ });
+
+ $('.btn-start').on('click', function(event)
+ {
+ bootbox.confirm(instanceNotices.confirmStart, function(result)
+ {
+ if(!result) return;
+
+ var loadingDialog = bootbox.dialog(
+ {
+ message: '
' + instanceNotices.starting + '
',
+ });
+
+ let id = $(event.target).closest('button').attr('instance-id');
+ let url = createLink('instance', 'ajaxStart', 'id=' + id, 'json');
+ $.post(url).done(function(response)
+ {
+ loadingDialog.modal('hide');
+
+ let res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ window.location.reload();
+ }
+ else
+ {
+ bootbox.alert(
+ {
+ title: instanceNotices.fail,
+ message: res.message,
+ });
+ }
+ });
+ });
+ });
+
+ $('.btn-stop').on('click', function(event)
+ {
+ bootbox.confirm(instanceNotices.confirmStop, function(result)
+ {
+ if(!result) return;
+
+ var loadingDialog = bootbox.dialog(
+ {
+ message: '
' + instanceNotices.stopping + '
',
+ });
+
+ let id = $(event.target).closest('button').attr('instance-id');
+ let url = createLink('instance', 'ajaxStop', 'id=' + id, 'json');
+ $.post(url).done(function(response)
+ {
+ loadingDialog.modal('hide');
+
+ let res = JSON.parse(response);
+ if(res.result == 'success')
+ {
+ window.location.reload();
+ }
+ else
+ {
+ bootbox.alert(
+ {
+ title: instanceNotices.fail,
+ message: res.message,
+ });
+ }
+ });
+ });
+ });
+
+ var enableTimer = true;
+ window.parent.$(window.parent.document).on('showapp', function(event, app)
+ {
+ enableTimer = app.code == 'space';
+ });
+
+ setInterval(function()
+ {
+ if(!enableTimer) return;
+
+ var statusURL = createLink('instance', 'ajaxStatus');
+ $.post(statusURL, {idList: instanceIdList}).done(function(response)
+ {
+ let res = JSON.parse(response);
+ if(res.result == 'success' && res.data instanceof Array)
+ {
+ res.data.forEach(function(instance)
+ {
+ if($(".instance-status[instance-id=" + instance.id + "]").data('status') != instance.status) window.location.reload();
+ });
+ }
+ if(res.locate) window.parent.location.href = res.locate;
+ });
+ }, 1000 * 5);
+
+ $('[data-toggle="tooltip"]').tooltip();
+});
diff --git a/module/system/lang/en.php b/module/system/lang/en.php
new file mode 100644
index 0000000000..4c5a8144b0
--- /dev/null
+++ b/module/system/lang/en.php
@@ -0,0 +1,268 @@
+system->dashboard = 'Dashboard';
+$lang->system->systemInfo = 'System information';
+$lang->system->dbManagement = 'Database management';
+$lang->system->ldapManagement = 'LDAP';
+$lang->system->dbList = 'Database list';
+$lang->system->dbName = 'Name';
+$lang->system->dbStatus = 'Status';
+$lang->system->dbType = 'Type';
+$lang->system->action = 'Action';
+$lang->system->management = 'Management';
+$lang->system->visit = 'Visit';
+$lang->system->close = 'Close';
+$lang->system->installLDAP = 'Install LDAP';
+$lang->system->editLDAP = 'Edit';
+$lang->system->LDAPInfo = 'LDAP information';
+$lang->system->accountInfo = 'Account information';
+$lang->system->advance = 'Senior';
+$lang->system->verify = 'Check';
+$lang->system->copy = 'Copy';
+$lang->system->copySuccess = '已复制到剪切板';
+$lang->system->cneStatus = '平台状态';
+$lang->system->cneStatistic = '资源统计';
+$lang->system->latestDynamic = '最新动态';
+$lang->system->nodeQuantity = '节点数';
+$lang->system->serviceQuantity = '服务数';
+$lang->system->cpuUsage = 'CPU(核)';
+$lang->system->memUsage = '内存(GB)';
+
+/* LDAP */
+$lang->system->LDAP = new stdclass;
+$lang->system->LDAP->info = 'LDAP信息';
+$lang->system->LDAP->ldapEnabled = '启用LDAP';
+$lang->system->LDAP->ldapQucheng = '渠成内置';
+$lang->system->LDAP->ldapSource = '来源';
+$lang->system->LDAP->ldapInstall = '安装并启用';
+$lang->system->LDAP->ldapUpdate = '更新';
+$lang->system->LDAP->accountInfo = '账号信息';
+$lang->system->LDAP->account = '账号';
+$lang->system->LDAP->password = '密码';
+$lang->system->LDAP->ldapUsername = '用户名';
+$lang->system->LDAP->ldapName = '名称';
+$lang->system->LDAP->host = '主机';
+$lang->system->LDAP->port = '端口';
+$lang->system->LDAP->account = '账号';
+$lang->system->LDAP->password = '密码';
+$lang->system->LDAP->ldapRoot = '根节点';
+$lang->system->LDAP->filterUser = '用户过滤';
+$lang->system->LDAP->email = '邮件字段';
+$lang->system->LDAP->extraAccount = '用户名字段';
+$lang->system->LDAP->ldapAdvance = '高级设置';
+$lang->system->LDAP->updateLDAP = '更新LDAP';
+$lang->system->LDAP->updateInstance = '更新已关联LDAP的服务';
+$lang->system->LDAP->updatingProgress = '更新中...剩余 %s 个服务。';
+
+$lang->system->ldapTypeList = array();
+$lang->system->ldapTypeList['qucheng'] = '渠成内置';
+$lang->system->ldapTypeList['extra'] = '外部映射';
+
+/* OSS */
+$lang->system->oss = new stdclass;
+$lang->system->oss->common = 'Object Storage';
+$lang->system->oss->appURL = 'Application address';
+$lang->system->oss->user = 'User name';
+$lang->system->oss->password = 'Password';
+$lang->system->oss->manage = 'Manage';
+$lang->system->oss->apiURL = 'API address';
+$lang->system->oss->accessKey = 'Access Key';
+$lang->system->oss->secretKey = 'Secret Key';
+
+/* SMTP */
+$lang->system->SMTP = new stdclass;
+$lang->system->SMTP->common = '邮箱配置';
+$lang->system->SMTP->enabled = '启用SMTP';
+$lang->system->SMTP->install = '安装';
+$lang->system->SMTP->update = '更新';
+$lang->system->SMTP->edit = '编辑';
+$lang->system->SMTP->editSMTP = '编辑SMTP';
+$lang->system->SMTP->account = '发信邮箱';
+$lang->system->SMTP->password = '密码';
+$lang->system->SMTP->host = 'SMTP服务器';
+$lang->system->SMTP->port = 'SMTP端口';
+$lang->system->SMTP->save = '保存';
+
+/* Domain */
+$lang->system->customDomain = '新域名';
+$lang->system->certPem = '公钥证书';
+$lang->system->certKey = '私钥';
+
+$lang->system->domain = new stdclass;
+$lang->system->domain->common = '域名管理';
+$lang->system->domain->editDomain = '修改域名配置';
+$lang->system->domain->config = '配置域名和证书';
+$lang->system->domain->currentDomain = '当前域名';
+$lang->system->domain->oldDomain = '旧域名';
+$lang->system->domain->newDomain = '新域名';
+$lang->system->domain->expiredDate = '证书过期时间';
+$lang->system->domain->uploadCert = '上传证书(仅支持泛域名证书)';
+
+$lang->system->domain->notReuseOldDomain = '使用自定义域名后无法改回默认域名';
+$lang->system->domain->setDNS = '建议修改域名前请先进行DNS解析,';
+$lang->system->domain->dnsHelperLink = '点击查看帮助文档';
+$lang->system->domain->updateInstancesDomain = '更新已安装服务的域名';
+$lang->system->domain->totalOldDomain = '共 %s 个。';
+$lang->system->domain->updatingProgress = '更新中...,剩余 %s 个,';
+$lang->system->domain->updating = '更新中...';
+
+$lang->system->SLB = new stdclass;
+$lang->system->SLB->common = '负载均衡';
+$lang->system->SLB->config = '配置负载均衡';
+$lang->system->SLB->edit = '修改负载均衡';
+$lang->system->SLB->ipPool = 'IP段';
+$lang->system->SLB->ipPoolExample = '示例:192.168.10.0/24或者192.168.10.0-192.168.10.100';
+$lang->system->SLB->installing = '正在配置负载均衡';
+$lang->system->SLB->leftSeconds = '预计剩余';
+$lang->system->SLB->second = '秒';
+
+$lang->system->notices = new stdclass;
+$lang->system->notices->success = '成功';
+$lang->system->notices->fail = '失败';
+$lang->system->notices->attention = '注意';
+$lang->system->notices->noLDAP = '找不到LDAP配置数据';
+$lang->system->notices->ldapUsed = '%s个服务已关联了LDAP';
+$lang->system->notices->ldapInstallSuccess = 'LDAP安装成功';
+$lang->system->notices->ldapUpdateSuccess = 'LDAP更新成功';
+$lang->system->notices->confirmUpdateLDAP = '修改LDAP后,会自动更新并重启已关联的服务,确定要修改吗?';
+$lang->system->notices->verifyLDAPSuccess = '校验LDAP成功!';
+$lang->system->notices->fillAllRequiredFields = '请填写全部必填项!';
+$lang->system->notices->smtpInstallSuccess = 'LDAP安装成功';
+$lang->system->notices->smtpUpdateSuccess = 'LDAP更新成功';
+$lang->system->notices->smtpWhiteList = '为防止邮件被屏蔽,请在邮件服务器里面将发信邮箱设为白名单';
+$lang->system->notices->smtpAuthCode = '有些邮箱要填写单独申请的授权码,具体请到邮箱相关设置查询';
+$lang->system->notices->smtpUsed = '%s 个服务关联了SMTP';
+$lang->system->notices->verifySMTPSuccess = '校验成功!';
+$lang->system->notices->pleaseCheckSMTPInfo = '校验失败!请检查用户名和密码是否正确';
+$lang->system->notices->confirmUpdateDomain = '修改域名后,会自动更新已安装服务的域名,确定要修改吗?';
+$lang->system->notices->updateDomainSuccess = '域名修改成功。';
+$lang->system->notices->configSLBSuccess = '配置负载均衡成功。';
+$lang->system->notices->validCert = '校验成功';
+
+$lang->system->errors = new stdclass;
+$lang->system->errors->notFoundDB = '找不到该数据库';
+$lang->system->errors->notFoundLDAP = '找不到LDAP数据';
+$lang->system->errors->dbNameIsEmpty = '数据库名为空';
+$lang->system->errors->notSupportedLDAP = '暂不支持该类型的LDAP';
+$lang->system->errors->failToInstallLDAP = '安装内置LDAP失败';
+$lang->system->errors->failToInstallExtraLDAP = '对接外部LDAP失败';
+$lang->system->errors->failToUpdateExtraLDAP = '更新外部LDAP失败';
+$lang->system->errors->failToUninstallQuChengLDAP = '卸载渠成内部LDAP失败';
+$lang->system->errors->failToUninstallExtraLDAP = '卸载外部LDAP失败';
+$lang->system->errors->failToDeleteLDAPSnippet = '删除LDAP片段失败';
+$lang->system->errors->verifyLDAPFailed = '校验LDAP失败';
+$lang->system->errors->LDAPLinked = '有服务已经关联了LDAP';
+$lang->system->errors->SMTPLinked = '有服务已经关联了SMTP服务';
+$lang->system->errors->failGetOssAccount = '获取对象存储账号失败';
+$lang->system->errors->failToInstallSMTP = '安装SMTP失败';
+$lang->system->errors->failToUninstallSMTP = '卸载SMTP失败';
+$lang->system->errors->failToUpdateSMTP = '更新SMTP失败';
+$lang->system->errors->verifySMTPFailed = '校验SMTP失败';
+$lang->system->errors->notFoundSMTPApp = '找不到SMTP代理应用';
+$lang->system->errors->notFoundSMTPService = '找不到SMTP代理服务';
+$lang->system->errors->domainIsRequired = '必须填写域名';
+$lang->system->errors->invalidDomain = '无效的域名或格式错误。域名只允许小写字母、数字、点(.)和中横线(-)';
+$lang->system->errors->failToUpdateDomain = '更新域名失败';
+$lang->system->errors->forbiddenOriginalDomain = '不能修改为平台默认域名';
+$lang->system->errors->newDomainIsSameWithOld = '新域名不能与原域名相同';
+$lang->system->errors->failedToConfigSLB = '配置负载均衡失败';
+$lang->system->errors->wrongIPRange = 'IP段格式错误,请参照示例格式,' . $lang->system->SLB->ipPoolExample;
+$lang->system->errors->ippoolRequired = 'IP段不能为空';
+$lang->system->errors->failedToInstallSLBComponent = '安装负载均衡组件失败';
+$lang->system->errors->tryReinstallSLB = '安装负载均衡组件超时,请重试。';
+
+$lang->system->backup = new stdclass();
+$lang->system->backup->common = '系统备份';
+$lang->system->backup->shortCommon = '备份';
+$lang->system->backup->systemInfo = '系统信息';
+$lang->system->backup->index = '备份首页';
+$lang->system->backup->history = '备份记录';
+$lang->system->backup->delete = '删除备份';
+$lang->system->backup->backup = '备份';
+$lang->system->backup->restore = '回滚';
+$lang->system->backup->change = '保留时间';
+$lang->system->backup->changeAB = '修改';
+$lang->system->backup->rmPHPHeader = '去除安全设置';
+$lang->system->backup->setting = '设置';
+$lang->system->backup->backupPerson = '备份人';
+$lang->system->backup->type = '备份类型';
+
+$lang->system->backup->settingAction = '备份设置';
+
+$lang->system->backup->name = '名称';
+$lang->system->backup->currentVersion = '当前版本';
+$lang->system->backup->latestVersion = '最新版本';
+
+$lang->system->backup->time = '备份时间';
+$lang->system->backup->files = '备份文件';
+$lang->system->backup->allCount = '总文件数';
+$lang->system->backup->count = '备份文件数';
+$lang->system->backup->size = '大小';
+$lang->system->backup->status = '状态';
+$lang->system->backup->running = '运行中';
+$lang->system->backup->done = '完成';
+
+$lang->system->backup->backupName = '备份名称:';
+$lang->system->backup->backupSql = '备份数据库:';
+$lang->system->backup->backupFile = '备份附件:';
+$lang->system->backup->restoreImage = '回滚平台镜像:';
+$lang->system->backup->restoreSQL = '回滚数据库:';
+$lang->system->backup->restoreFile = '回滚附件:';
+$lang->system->backup->checkService = '检查服务:';
+
+$lang->system->backup->upgrade = '升级';
+$lang->system->backup->rollback = '回滚';
+$lang->system->backup->restart = '重启';
+$lang->system->backup->delete = '删除';
+
+$lang->system->backup->statusList['success'] = '成功';
+$lang->system->backup->statusList['fail'] = '失败';
+
+$lang->system->backup->restoreProgress['doing'] = '进行中';
+$lang->system->backup->restoreProgress['done'] = '完成';
+
+$lang->system->backup->typeList['manual'] = '手动备份';
+$lang->system->backup->typeList['upgrade'] = '升级前自动备份';
+$lang->system->backup->typeList['restore'] = '回滚前自动备份';
+
+$lang->system->backup->settingDir = '备份目录';
+$lang->system->backup->settingList['nofile'] = '不备份附件和代码';
+$lang->system->backup->settingList['nosafe'] = '不需要防下载PHP文件头';
+
+$lang->system->backup->waitting = '
正在进行中,请稍候...';
+$lang->system->backup->progressSQL = '备份中,已备份%s';
+$lang->system->backup->progressAttach = '备份中,共有%s个文件,已经备份%s个';
+$lang->system->backup->progressCode = '代码备份中,共有%s个文件,已经备份%s个';
+$lang->system->backup->confirmDelete = '是否删除该备份?';
+$lang->system->backup->confirmRestore = '请确认是否回滚?';
+$lang->system->backup->holdDays = '备份保留最近 %s 天';
+$lang->system->backup->copiedFail = '复制失败的文件:';
+$lang->system->backup->restoreTip = '还原功能只还原数据库。';
+$lang->system->backup->versionInfo = '点击查看新版本介绍';
+$lang->system->backup->confirmUpgrade = '请确认是否升级渠成平台?';
+$lang->system->backup->upgrading = '升级中';
+$lang->system->backup->backupTitle = '正在备份 渠成平台...';
+$lang->system->backup->restoreTitle = '正在回滚 渠成平台...';
+$lang->system->backup->backingUp = '进行中';
+$lang->system->backup->restoring = '进行中';
+
+$lang->system->backup->success = new stdclass();
+$lang->system->backup->success->backup = '备份成功!';
+$lang->system->backup->success->restore = '还原成功!';
+$lang->system->backup->success->upgrade = '升级成功!';
+$lang->system->backup->success->degrade = '降级成功!';
+
+$lang->system->backup->error = new stdclass();
+$lang->system->backup->error->noCreateDir = '备份目录不存在,也无法创建该目录';
+$lang->system->backup->error->noWritable = "
%s 不可写!请检查该目录权限,否则无法备份。";
+$lang->system->backup->error->noDelete = "文件 %s 无法删除,修改权限或手工删除。";
+$lang->system->backup->error->restoreSQL = "数据库还原失败,错误:%s";
+$lang->system->backup->error->restoreFile = "附件还原失败,错误:%s";
+$lang->system->backup->error->backupFile = "附件备份失败,错误:%s";
+$lang->system->backup->error->backupCode = "代码备份失败,错误:%s";
+
+$lang->system->backup->error->upgradeFail = "升级失败!";
+$lang->system->backup->error->upgradeOvertime = "升级超时!";
+$lang->system->backup->error->degradeFail = "降级失败!";
+$lang->system->backup->error->beenLatestVersion = "已经是最新版,无需升级!";
+$lang->system->backup->error->requireVersion = "必须上传版本号!";
+
diff --git a/module/system/lang/zh-cn.php b/module/system/lang/zh-cn.php
new file mode 100644
index 0000000000..3f20251bee
--- /dev/null
+++ b/module/system/lang/zh-cn.php
@@ -0,0 +1,268 @@
+system->dashboard = '仪表盘';
+$lang->system->systemInfo = '系统信息';
+$lang->system->dbManagement = '数据库管理';
+$lang->system->ldapManagement = 'LDAP';
+$lang->system->dbList = '数据库列表';
+$lang->system->dbName = '名称';
+$lang->system->dbStatus = '状态';
+$lang->system->dbType = '类型';
+$lang->system->action = '操作';
+$lang->system->management = '管理';
+$lang->system->visit = '访问';
+$lang->system->close = '关闭';
+$lang->system->installLDAP = '安装LDAP';
+$lang->system->editLDAP = '编辑';
+$lang->system->LDAPInfo = 'LDAP信息';
+$lang->system->accountInfo = '账号信息';
+$lang->system->advance = '高级';
+$lang->system->verify = '校验';
+$lang->system->copy = '复制';
+$lang->system->copySuccess = '已复制到剪切板';
+$lang->system->cneStatus = '平台状态';
+$lang->system->cneStatistic = '资源统计';
+$lang->system->latestDynamic = '最新动态';
+$lang->system->nodeQuantity = '节点数';
+$lang->system->serviceQuantity = '服务数';
+$lang->system->cpuUsage = 'CPU(核)';
+$lang->system->memUsage = '内存(GB)';
+
+/* LDAP */
+$lang->system->LDAP = new stdclass;
+$lang->system->LDAP->info = 'LDAP信息';
+$lang->system->LDAP->ldapEnabled = '启用LDAP';
+$lang->system->LDAP->ldapQucheng = '渠成内置';
+$lang->system->LDAP->ldapSource = '来源';
+$lang->system->LDAP->ldapInstall = '安装并启用';
+$lang->system->LDAP->ldapUpdate = '更新';
+$lang->system->LDAP->accountInfo = '账号信息';
+$lang->system->LDAP->account = '账号';
+$lang->system->LDAP->password = '密码';
+$lang->system->LDAP->ldapUsername = '用户名';
+$lang->system->LDAP->ldapName = '名称';
+$lang->system->LDAP->host = '主机';
+$lang->system->LDAP->port = '端口';
+$lang->system->LDAP->account = '账号';
+$lang->system->LDAP->password = '密码';
+$lang->system->LDAP->ldapRoot = '根节点';
+$lang->system->LDAP->filterUser = '用户过滤';
+$lang->system->LDAP->email = '邮件字段';
+$lang->system->LDAP->extraAccount = '用户名字段';
+$lang->system->LDAP->ldapAdvance = '高级设置';
+$lang->system->LDAP->updateLDAP = '更新LDAP';
+$lang->system->LDAP->updateInstance = '更新已关联LDAP的服务';
+$lang->system->LDAP->updatingProgress = '更新中...剩余 %s 个服务。';
+
+$lang->system->ldapTypeList = array();
+$lang->system->ldapTypeList['qucheng'] = '渠成内置';
+$lang->system->ldapTypeList['extra'] = '外部映射';
+
+/* OSS */
+$lang->system->oss = new stdclass;
+$lang->system->oss->common = '对象存储';
+$lang->system->oss->appURL = '应用地址';
+$lang->system->oss->user = '用户名';
+$lang->system->oss->password = '密码';
+$lang->system->oss->manage = '管理';
+$lang->system->oss->apiURL = 'API地址';
+$lang->system->oss->accessKey = 'Access Key';
+$lang->system->oss->secretKey = 'Secret Key';
+
+/* SMTP */
+$lang->system->SMTP = new stdclass;
+$lang->system->SMTP->common = '邮箱配置';
+$lang->system->SMTP->enabled = '启用SMTP';
+$lang->system->SMTP->install = '安装';
+$lang->system->SMTP->update = '更新';
+$lang->system->SMTP->edit = '编辑';
+$lang->system->SMTP->editSMTP = '编辑SMTP';
+$lang->system->SMTP->account = '发信邮箱';
+$lang->system->SMTP->password = '密码';
+$lang->system->SMTP->host = 'SMTP服务器';
+$lang->system->SMTP->port = 'SMTP端口';
+$lang->system->SMTP->save = '保存';
+
+/* Domain */
+$lang->system->customDomain = '新域名';
+$lang->system->certPem = '公钥证书';
+$lang->system->certKey = '私钥';
+
+$lang->system->domain = new stdclass;
+$lang->system->domain->common = '域名管理';
+$lang->system->domain->editDomain = '修改域名配置';
+$lang->system->domain->config = '配置域名和证书';
+$lang->system->domain->currentDomain = '当前域名';
+$lang->system->domain->oldDomain = '旧域名';
+$lang->system->domain->newDomain = '新域名';
+$lang->system->domain->expiredDate = '证书过期时间';
+$lang->system->domain->uploadCert = '上传证书(仅支持泛域名证书)';
+
+$lang->system->domain->notReuseOldDomain = '使用自定义域名后无法改回默认域名';
+$lang->system->domain->setDNS = '建议修改域名前请先进行DNS解析,';
+$lang->system->domain->dnsHelperLink = '点击查看帮助文档';
+$lang->system->domain->updateInstancesDomain = '更新已安装服务的域名';
+$lang->system->domain->totalOldDomain = '共 %s 个。';
+$lang->system->domain->updatingProgress = '更新中...,剩余 %s 个,';
+$lang->system->domain->updating = '更新中...';
+
+$lang->system->SLB = new stdclass;
+$lang->system->SLB->common = '负载均衡';
+$lang->system->SLB->config = '配置负载均衡';
+$lang->system->SLB->edit = '修改负载均衡';
+$lang->system->SLB->ipPool = 'IP段';
+$lang->system->SLB->ipPoolExample = '示例:192.168.10.0/24或者192.168.10.0-192.168.10.100';
+$lang->system->SLB->installing = '正在配置负载均衡';
+$lang->system->SLB->leftSeconds = '预计剩余';
+$lang->system->SLB->second = '秒';
+
+$lang->system->notices = new stdclass;
+$lang->system->notices->success = '成功';
+$lang->system->notices->fail = '失败';
+$lang->system->notices->attention = '注意';
+$lang->system->notices->noLDAP = '找不到LDAP配置数据';
+$lang->system->notices->ldapUsed = '%s个服务已关联了LDAP';
+$lang->system->notices->ldapInstallSuccess = 'LDAP安装成功';
+$lang->system->notices->ldapUpdateSuccess = 'LDAP更新成功';
+$lang->system->notices->confirmUpdateLDAP = '修改LDAP后,会自动更新并重启已关联的服务,确定要修改吗?';
+$lang->system->notices->verifyLDAPSuccess = '校验LDAP成功!';
+$lang->system->notices->fillAllRequiredFields = '请填写全部必填项!';
+$lang->system->notices->smtpInstallSuccess = 'LDAP安装成功';
+$lang->system->notices->smtpUpdateSuccess = 'LDAP更新成功';
+$lang->system->notices->smtpWhiteList = '为防止邮件被屏蔽,请在邮件服务器里面将发信邮箱设为白名单';
+$lang->system->notices->smtpAuthCode = '有些邮箱要填写单独申请的授权码,具体请到邮箱相关设置查询';
+$lang->system->notices->smtpUsed = '%s 个服务关联了SMTP';
+$lang->system->notices->verifySMTPSuccess = '校验成功!';
+$lang->system->notices->pleaseCheckSMTPInfo = '校验失败!请检查用户名和密码是否正确';
+$lang->system->notices->confirmUpdateDomain = '修改域名后,会自动更新已安装服务的域名,确定要修改吗?';
+$lang->system->notices->updateDomainSuccess = '域名修改成功。';
+$lang->system->notices->configSLBSuccess = '配置负载均衡成功。';
+$lang->system->notices->validCert = '校验成功';
+
+$lang->system->errors = new stdclass;
+$lang->system->errors->notFoundDB = '找不到该数据库';
+$lang->system->errors->notFoundLDAP = '找不到LDAP数据';
+$lang->system->errors->dbNameIsEmpty = '数据库名为空';
+$lang->system->errors->notSupportedLDAP = '暂不支持该类型的LDAP';
+$lang->system->errors->failToInstallLDAP = '安装内置LDAP失败';
+$lang->system->errors->failToInstallExtraLDAP = '对接外部LDAP失败';
+$lang->system->errors->failToUpdateExtraLDAP = '更新外部LDAP失败';
+$lang->system->errors->failToUninstallQuChengLDAP = '卸载渠成内部LDAP失败';
+$lang->system->errors->failToUninstallExtraLDAP = '卸载外部LDAP失败';
+$lang->system->errors->failToDeleteLDAPSnippet = '删除LDAP片段失败';
+$lang->system->errors->verifyLDAPFailed = '校验LDAP失败';
+$lang->system->errors->LDAPLinked = '有服务已经关联了LDAP';
+$lang->system->errors->SMTPLinked = '有服务已经关联了SMTP服务';
+$lang->system->errors->failGetOssAccount = '获取对象存储账号失败';
+$lang->system->errors->failToInstallSMTP = '安装SMTP失败';
+$lang->system->errors->failToUninstallSMTP = '卸载SMTP失败';
+$lang->system->errors->failToUpdateSMTP = '更新SMTP失败';
+$lang->system->errors->verifySMTPFailed = '校验SMTP失败';
+$lang->system->errors->notFoundSMTPApp = '找不到SMTP代理应用';
+$lang->system->errors->notFoundSMTPService = '找不到SMTP代理服务';
+$lang->system->errors->domainIsRequired = '必须填写域名';
+$lang->system->errors->invalidDomain = '无效的域名或格式错误。域名只允许小写字母、数字、点(.)和中横线(-)';
+$lang->system->errors->failToUpdateDomain = '更新域名失败';
+$lang->system->errors->forbiddenOriginalDomain = '不能修改为平台默认域名';
+$lang->system->errors->newDomainIsSameWithOld = '新域名不能与原域名相同';
+$lang->system->errors->failedToConfigSLB = '配置负载均衡失败';
+$lang->system->errors->wrongIPRange = 'IP段格式错误,请参照示例格式,' . $lang->system->SLB->ipPoolExample;
+$lang->system->errors->ippoolRequired = 'IP段不能为空';
+$lang->system->errors->failedToInstallSLBComponent = '安装负载均衡组件失败';
+$lang->system->errors->tryReinstallSLB = '安装负载均衡组件超时,请重试。';
+
+$lang->system->backup = new stdclass();
+$lang->system->backup->common = '系统备份';
+$lang->system->backup->shortCommon = '备份';
+$lang->system->backup->systemInfo = '系统信息';
+$lang->system->backup->index = '备份首页';
+$lang->system->backup->history = '备份记录';
+$lang->system->backup->delete = '删除备份';
+$lang->system->backup->backup = '备份';
+$lang->system->backup->restore = '回滚';
+$lang->system->backup->change = '保留时间';
+$lang->system->backup->changeAB = '修改';
+$lang->system->backup->rmPHPHeader = '去除安全设置';
+$lang->system->backup->setting = '设置';
+$lang->system->backup->backupPerson = '备份人';
+$lang->system->backup->type = '备份类型';
+
+$lang->system->backup->settingAction = '备份设置';
+
+$lang->system->backup->name = '名称';
+$lang->system->backup->currentVersion = '当前版本';
+$lang->system->backup->latestVersion = '最新版本';
+
+$lang->system->backup->time = '备份时间';
+$lang->system->backup->files = '备份文件';
+$lang->system->backup->allCount = '总文件数';
+$lang->system->backup->count = '备份文件数';
+$lang->system->backup->size = '大小';
+$lang->system->backup->status = '状态';
+$lang->system->backup->running = '运行中';
+$lang->system->backup->done = '完成';
+
+$lang->system->backup->backupName = '备份名称:';
+$lang->system->backup->backupSql = '备份数据库:';
+$lang->system->backup->backupFile = '备份附件:';
+$lang->system->backup->restoreImage = '回滚平台镜像:';
+$lang->system->backup->restoreSQL = '回滚数据库:';
+$lang->system->backup->restoreFile = '回滚附件:';
+$lang->system->backup->checkService = '检查服务:';
+
+$lang->system->backup->upgrade = '升级';
+$lang->system->backup->rollback = '回滚';
+$lang->system->backup->restart = '重启';
+$lang->system->backup->delete = '删除';
+
+$lang->system->backup->statusList['success'] = '成功';
+$lang->system->backup->statusList['fail'] = '失败';
+
+$lang->system->backup->restoreProgress['doing'] = '进行中';
+$lang->system->backup->restoreProgress['done'] = '完成';
+
+$lang->system->backup->typeList['manual'] = '手动备份';
+$lang->system->backup->typeList['upgrade'] = '升级前自动备份';
+$lang->system->backup->typeList['restore'] = '回滚前自动备份';
+
+$lang->system->backup->settingDir = '备份目录';
+$lang->system->backup->settingList['nofile'] = '不备份附件和代码';
+$lang->system->backup->settingList['nosafe'] = '不需要防下载PHP文件头';
+
+$lang->system->backup->waitting = '
正在进行中,请稍候...';
+$lang->system->backup->progressSQL = '备份中,已备份%s';
+$lang->system->backup->progressAttach = '备份中,共有%s个文件,已经备份%s个';
+$lang->system->backup->progressCode = '代码备份中,共有%s个文件,已经备份%s个';
+$lang->system->backup->confirmDelete = '是否删除该备份?';
+$lang->system->backup->confirmRestore = '请确认是否回滚?';
+$lang->system->backup->holdDays = '备份保留最近 %s 天';
+$lang->system->backup->copiedFail = '复制失败的文件:';
+$lang->system->backup->restoreTip = '还原功能只还原数据库。';
+$lang->system->backup->versionInfo = '点击查看新版本介绍';
+$lang->system->backup->confirmUpgrade = '请确认是否升级渠成平台?';
+$lang->system->backup->upgrading = '升级中';
+$lang->system->backup->backupTitle = '正在备份 渠成平台...';
+$lang->system->backup->restoreTitle = '正在回滚 渠成平台...';
+$lang->system->backup->backingUp = '进行中';
+$lang->system->backup->restoring = '进行中';
+
+$lang->system->backup->success = new stdclass();
+$lang->system->backup->success->backup = '备份成功!';
+$lang->system->backup->success->restore = '还原成功!';
+$lang->system->backup->success->upgrade = '升级成功!';
+$lang->system->backup->success->degrade = '降级成功!';
+
+$lang->system->backup->error = new stdclass();
+$lang->system->backup->error->noCreateDir = '备份目录不存在,也无法创建该目录';
+$lang->system->backup->error->noWritable = "
%s 不可写!请检查该目录权限,否则无法备份。";
+$lang->system->backup->error->noDelete = "文件 %s 无法删除,修改权限或手工删除。";
+$lang->system->backup->error->restoreSQL = "数据库还原失败,错误:%s";
+$lang->system->backup->error->restoreFile = "附件还原失败,错误:%s";
+$lang->system->backup->error->backupFile = "附件备份失败,错误:%s";
+$lang->system->backup->error->backupCode = "代码备份失败,错误:%s";
+
+$lang->system->backup->error->upgradeFail = "升级失败!";
+$lang->system->backup->error->upgradeOvertime = "升级超时!";
+$lang->system->backup->error->degradeFail = "降级失败!";
+$lang->system->backup->error->beenLatestVersion = "已经是最新版,无需升级!";
+$lang->system->backup->error->requireVersion = "必须上传版本号!";
+
diff --git a/module/system/model.php b/module/system/model.php
new file mode 100644
index 0000000000..e07310cf5f
--- /dev/null
+++ b/module/system/model.php
@@ -0,0 +1,1128 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+class systemModel extends model
+{
+ /**
+ * Construct function: load setting model.
+ *
+ * @access public
+ * @return mixed
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ $this->loadModel('setting');
+ }
+
+ /**
+ * Get LDAP menu switcher.
+ *
+ * @access public
+ * @return string
+ */
+ public function getLDAPSwitcher()
+ {
+ $output = "";
+
+ return $output;
+ }
+
+ /**
+ * Get Oss menu switcher.
+ *
+ * @access public
+ * @return string
+ */
+ public function getOssSwitcher()
+ {
+ $output = "";
+
+ return $output;
+ }
+
+ /**
+ * Get SMTP menu switcher.
+ *
+ * @access public
+ * @return string
+ */
+ public function getSMTPSwitcher()
+ {
+ $output = "";
+
+ return $output;
+ }
+
+ /**
+ * Print action buttons.
+ *
+ * @param object $db
+ * @access public
+ * @return void
+ */
+ public function printDBAction($db)
+ {
+ $disabled = strtolower($db->status) == 'running' ? '' : 'disabled';
+ $btnHtml = html::commonButton($this->lang->system->management, "{$disabled} data-db-name='{$db->name}' data-db-type='{$db->db_type}' data-namespace='{$db->namespace}'", 'db-login btn btn-primary');
+
+ echo $btnHtml;
+ }
+
+ /**
+ * Print edit LDAP button.
+ *
+ * @access public
+ * @return void
+ */
+ public function printEditLDAPBtn()
+ {
+ $this->loadModel('instance');
+
+ $title = $this->lang->system->editLDAP;
+ $toolTips = '';
+ $count = $this->instance->countLDAP();
+ if($count)
+ {
+ $title = sprintf($this->lang->system->notices->ldapUsed, $count);
+ $toolTips = "data-toggle='tooltip' data-placement='bottom'";
+ }
+
+ $buttonHtml = '';
+ $buttonHtml .= "
";
+ $buttonHtml .= html::a(inLink('editLDAP'), $this->lang->system->editLDAP, '', "title='{$title}' class='btn-edit btn label label-outline label-primary label-lg'");
+ $buttonHtml .= "";
+
+ echo $buttonHtml;
+ }
+
+ /**
+ * Print LDAP buttons.
+ *
+ * @param objevt $ldapInstance
+ * @access public
+ * @return mixed
+ */
+ public function printLDAPButtons($ldapInstance)
+ {
+ $this->loadModel('instance');
+ $this->app->loadLang('instance');
+
+ $buttonHtml = '';
+
+ if($ldapInstance->domain)
+ {
+ $disableVisit = !$this->instance->canDo('visit', $ldapInstance);
+ $buttonHtml .= html::commonButton($this->lang->instance->visit, "instance-id='{$ldapInstance->id}' title='{$this->lang->instance->visit}'" . ($disableVisit ? ' disabled ' : ''), 'btn-visit btn label label-outline label-primary label-lg');
+ }
+
+ $disableStart = !$this->instance->canDo('start', $ldapInstance);
+ $buttonHtml .= html::commonButton($this->lang->instance->start, "instance-id='{$ldapInstance->id}' title='{$this->lang->instance->start}'" . ($disableStart ? ' disabled ' : ''), "btn-start btn label label-outline label-primary label-lg");
+
+ $title = $this->lang->instance->stop;
+ $toolTips = '';
+ $count = $this->instance->countLDAP();
+ if($count)
+ {
+ $title = $this->lang->system->notices->ldapUsed;
+ $toolTips = "data-toggle='tooltip' data-placement='bottom' runat='server'";
+ }
+
+ $disableStop = $count > 0 || !$this->instance->canDo('stop', $ldapInstance);
+ $buttonHtml .= "
";
+ $buttonHtml .= html::commonButton($this->lang->instance->stop, "instance-id='{$ldapInstance->id}' title='{$title}'" . ($disableStop ? ' disabled ' : ''), 'btn-stop btn label label-outline label-danger label-lg');
+ $buttonHtml .= "";
+
+ echo $buttonHtml;
+ }
+
+ /**
+ * Install QuCheng LDAP.
+ *
+ * @param string $channel
+ * @access public
+ * @return bool|object
+ */
+ public function installQuchengLDAP($ldapApp, $channel)
+ {
+ return $this->loadModel('instance')->installLDAP($ldapApp, '', 'OpenLDAP', $k8name = '', $channel);
+ }
+
+ /**
+ * Update Qucheng LDAP.
+ *
+ * @param object $ldapApp
+ * @param string $channel
+ * @access public
+ * @return string
+ */
+ public function updateQuchengLDAP($ldapApp, $channel)
+ {
+ $instanceID = $this->setting->getItem('owner=system&module=common§ion=ldap&key=instanceID');
+ if($instanceID)
+ {
+ /* If QuCheng internal LDAP has been installed. Set QuCheng LDAP to be active. */
+ return $this->setting->setItem('system.common.ldap.active', 'qucheng');
+ }
+ else
+ {
+ /* If QuCheng internal LDAP has not been installed. */
+ return $this->installQuchengLDAP($ldapApp, $channel);
+ }
+ }
+
+ /**
+ * Install or update extra LDAP: it is creating a snippet in k8s system in fact.
+ *
+ * @param object $settings
+ * @access protected
+ * @return bool
+ */
+ public function configExtraLDAP($settings)
+ {
+ if(!$this->testLDAPConnection($settings))
+ {
+ dao::$errors[] = $this->lang->system->notSupportedLDAP;
+ return false;
+ }
+
+ $space = $this->loadModel('space')->getSystemSpace($this->app->user->account);
+
+ $snippetSettings = new stdclass;
+ $snippetSettings->name = 'snippet-extra-ldap';
+ $snippetSettings->namespace = $space->k8space;
+ $snippetSettings->auto_import = false;
+
+ $snippetSettings->values = new stdclass;
+ $snippetSettings->values->auth = new stdclass;
+ $snippetSettings->values->auth->ldap = new stdclass;
+ $snippetSettings->values->auth->ldap->enabled = true;
+ $snippetSettings->values->auth->ldap->type = 'ldap';
+ $snippetSettings->values->auth->ldap->host = $settings->host;
+ $snippetSettings->values->auth->ldap->port = strval($settings->port);
+ $snippetSettings->values->auth->ldap->bindDN = "cn={$settings->bindDN},{$settings->baseDN}";
+ $snippetSettings->values->auth->ldap->bindPass = $settings->bindPass;
+ $snippetSettings->values->auth->ldap->baseDN = $settings->baseDN;
+ $snippetSettings->values->auth->ldap->filter = html_entity_decode($settings->filter);
+ $snippetSettings->values->auth->ldap->attrUser = $settings->attrUser;
+ $snippetSettings->values->auth->ldap->attrEmail = $settings->attrEmail;
+
+ $exists = $this->getExtraLDAPSettings();
+ if(empty($exists))
+ {
+ $snippetResult = $this->loadModel('cne')->addSnippet($snippetSettings);
+ if($snippetResult->code != 200)
+ {
+ dao::$errors[] = $this->lang->system->errors->failToInstallExtraLDAP;
+ return false;
+ }
+ }
+ else
+ {
+ $snippetResult = $this->loadModel('cne')->updateSnippet($snippetSettings);
+ if($snippetResult->code != 200)
+ {
+ dao::$errors[] = $this->lang->system->errors->failToUpdateExtraLDAP;
+ return false;
+ }
+ }
+
+ /* Save extra LDAP setting to database. */
+ $secretKey = helper::readKey();
+ $encryptedPassword = openssl_encrypt($snippetSettings->values->auth->ldap->bindPass, 'DES-ECB', $secretKey);
+ $settings->bindPass = $encryptedPassword;
+
+ $this->setting->setItem('system.common.ldap.active', 'extra');
+ $this->setting->setItem('system.common.ldap.extraSnippetName', $snippetSettings->name); // Parameter for App installation API.
+ $this->setting->setItem('system.common.ldap.extraSettings', json_encode($settings));
+
+ return true;
+ }
+
+ /**
+ * Update LDAP config and update instance.
+ *
+ * @param object $ldapApp
+ * @param string $channel
+ * @access public
+ * @return void
+ */
+ public function updateLDAP($ldapApp, $channel)
+ {
+ $postData = fixer::input('post')->setDefault('source', 'qucheng')->get();
+ if($postData->source == 'qucheng')
+ {
+ $success = $this->updateQuchengLDAP($ldapApp, $channel);
+ }
+ else if($postData->source == 'extra')
+ {
+ $success = $this->configExtraLDAP((object)$postData->extra);
+ if($success) $this->uninstallQuChengLDAP(true);
+ }
+
+ if(!$success) return false;
+
+ /* Update instances that has been enabled LDAP. */
+ $instanceList = $this->loadModel('instance')->getListEnabledLDAP();
+ $counter = count($instanceList);
+ foreach($instanceList as $instance)
+ {
+ $this->loadModel('setting')->setItem('system.common.ldap.updatingProgress', $counter);
+ $this->instance->switchLDAP($instance, true);
+ $counter-- ;
+ }
+
+ $this->loadModel('setting')->deleteItems('owner=system&module=common§ion=ldap&key=updatingProgress');
+ return true;
+ }
+
+ /**
+ * Uninstall QuCheng LDAP.
+ *
+ * @param bool $force
+ * @access public
+ * @return bool
+ */
+ public function uninstallQuChengLDAP($force = false)
+ {
+ if(!$force)
+ {
+ $ldapLinked = $this->loadModel('instance')->countLDAP();
+ if($ldapLinked)
+ {
+ dao::$errors[] = $this->lang->system->errors->LDAPLinked;
+ return false;
+ }
+ }
+
+ $instanceID = $this->setting->getItem('owner=system&module=common§ion=ldap&key=instanceID');
+ $instance = $this->loadModel('instance')->getByID($instanceID);
+ if($instance)
+ {
+ /* 1. Uninstall QuCheng LDAP service. */
+ if(!$this->loadModel('instance')->uninstall($instance))
+ {
+ dao::$errors[] = $this->lang->system->errors->failToUninstallQuChengLDAP;
+ return false;
+ }
+
+ /* 2. Remove snippet config map from CNE. */
+ $space = $this->loadModel('space')->getSystemSpace($this->app->user->account);
+
+ $apiParams = new stdclass;
+ $apiParams->name = 'snippet-qucheng-ldap';
+ $apiParams->namespace = $space->k8space;
+
+ $result = $this->loadModel('cne')->removeSnippet($apiParams);
+ if($result->code != 200)
+ {
+ dao::$errors[] = $this->lang->system->errors->failToUninstallQuChengLDAP;
+ return false;
+ }
+ }
+
+ /* 3. Delete LDAP settings in database. */
+ $this->setting->deleteItems('owner=system&module=common§ion=ldap&key=instanceID');
+ $this->setting->deleteItems('owner=system&module=common§ion=ldap&key=snippetName');
+ if($this->getActiveLDAP() == 'qucheng') $this->setting->deleteItems('owner=system&module=common§ion=ldap&key=active');
+
+ return true;
+ }
+
+ /**
+ * Uninstall extra LDAP.
+ *
+ * @access public
+ * @return bool
+ */
+ public function uninstallExtraLDAP()
+ {
+ $ldapLinked = $this->loadModel('instance')->countLDAP();
+ if($ldapLinked)
+ {
+ dao::$errors[] = $this->lang->system->errors->LDAPLinked;
+ return false;
+ }
+
+ /* 1. Remove snippet config map from CNE. */
+ $space = $this->loadModel('space')->getSystemSpace($this->app->user->account);
+
+ $apiParams = new stdclass;
+ $apiParams->name = 'snippet-extra-ldap';
+ $apiParams->namespace = $space->k8space;
+
+ $result = $this->loadModel('cne')->removeSnippet($apiParams);
+ if($result->code != 200)
+ {
+ dao::$errors[] = $this->lang->system->errors->failToDeleteLDAPSnippet;
+ return false;
+ }
+
+ /* 2. Delete extra LDAP settings in database. */
+ $this->setting->deleteItems('owner=system&module=common§ion=ldap&key=extraSettings');
+ $this->setting->deleteItems('owner=system&module=common§ion=ldap&key=extraSnippetName');
+ if($this->getActiveLDAP() == 'extra') $this->setting->deleteItems('owner=system&module=common§ion=ldap&key=active');
+
+ return true;
+ }
+
+ /**
+ * Get extra LDAP settings.
+ *
+ * @access public
+ * @return object|array
+ */
+ public function getExtraLDAPSettings()
+ {
+ $settings = $this->setting->getItem('owner=system&module=common§ion=ldap&key=extraSettings');
+ $settings = @json_decode($settings);
+ if(empty($settings)) return array();
+
+ $secretKey = helper::readKey();
+ $settings->bindPass = openssl_decrypt($settings->bindPass, 'DES-ECB', $secretKey);
+ return $settings;
+ }
+
+ /**
+ * Test LDAP Connection by post settings.
+ *
+ * @param object $settings
+ * @access public
+ * @return bool
+ */
+ public function testLDAPConnection($settings)
+ {
+ $connectID = ldap_connect("ldap://{$settings->host}:{$settings->port}");
+
+ if(!ldap_set_option($connectID, LDAP_OPT_PROTOCOL_VERSION, 3)) return false;
+
+ return ldap_bind($connectID, "cn={$settings->bindDN},{$settings->baseDN}", $settings->bindPass);
+ }
+
+ /**
+ * Has installed global LDAP or not.
+ *
+ * @access public
+ * @return bool
+ */
+ public function hasSystemLDAP()
+ {
+ $activeLDAP = $this->setting->getItem('owner=system&module=common§ion=ldap&key=active');
+ return $activeLDAP == 'extra' or $activeLDAP == 'qucheng'; // LDAP has been installed.
+ }
+
+ /**
+ * Get global LDAP snippet name.
+ *
+ * @access public
+ * @return string
+ */
+ public function ldapSnippetName()
+ {
+ $activeLDAP = $this->setting->getItem('owner=system&module=common§ion=ldap&key=active');
+
+ if($activeLDAP == 'extra') return $this->setting->getItem('owner=system&module=common§ion=ldap&key=extraSnippetName');
+
+ return $this->setting->getItem('owner=system&module=common§ion=ldap&key=snippetName');
+ }
+
+ /**
+ * Get active LDAP type.
+ *
+ * @access public
+ * @return string|null
+ */
+ public function getActiveLDAP()
+ {
+ return $this->setting->getItem('owner=system&module=common§ion=ldap&key=active');
+ }
+
+ /**
+ * Check global SMTP is enabled or not.
+ *
+ * @access public
+ * @return bool
+ */
+ public function isSMTPEnabled()
+ {
+ $smtpSnippetName = $this->smtpSnippetName();
+ $enabled = $this->setting->getItem('owner=system&module=common§ion=smtp&key=enabled');
+
+ return $smtpSnippetName && $enabled;
+ }
+
+ /**
+ * Get SMTP snippet name.
+ *
+ * @access public
+ * @return string
+ */
+ public function smtpSnippetName()
+ {
+ return $this->setting->getItem('owner=system&module=common§ion=smtp&key=snippetName');
+ }
+
+ /**
+ * Get SMTP settings.
+ *
+ * @access public
+ * @return object
+ */
+ public function getSMTPSettings()
+ {
+ $settingMap = json_decode($this->setting->getItem('owner=system&module=common§ion=smtp&key=settingsMap'));
+ if(empty($settingMap)) return new stdclass;
+
+ $settings = $settingMap->env;
+ $settings->SMTP_PASS = openssl_decrypt($settings->SMTP_PASS, 'DES-ECB', helper::readKey());
+
+ //$snippetSettings = json_decode($this->setting->getItem('owner=system&module=common§ion=smtp&key=snippetSettings'));
+
+ return $settings;
+ }
+
+ /**
+ * Update SMTP settings.
+ *
+ * @access public
+ * @return void
+ */
+ public function updateSMTPSettings()
+ {
+ $this->loadModel('cne');
+ $this->loadModel('instance');
+
+ $channel = $this->app->session->cloudChannel ? $this->app->session->cloudChannel : $this->config->cloud->api->channel;
+
+ $smtpSettings = fixer::input('post')->get();
+
+ $instanceID = $this->setting->getItem('owner=system&module=common§ion=smtp&key=instanceID');
+ $instance = $this->instance->getByID($instanceID);
+ if(!$instance)
+ {
+ dao::$errors[] = $this->lang->system->errors->notFoundSMTPService;
+ return false;
+ }
+
+ $instance->version = 'latest'; // Update and upgrade SMTP proxy instance.
+
+ /* 1. Update SMTP service settings. */
+ $settingsMap = new stdclass;
+ $settingsMap->env = new stdclass;
+ $settingsMap->env->SMTP_HOST = $smtpSettings->host;
+ $settingsMap->env->SMTP_PORT = strval($smtpSettings->port);
+ $settingsMap->env->SMTP_USER = $smtpSettings->user;
+ $settingsMap->env->SMTP_PASS = $smtpSettings->pass;
+
+ $settings = new stdclass;
+ $settings->settings_map = $settingsMap;
+
+ $success = $this->loadModel('cne')->updateConfig($instance, $settings);
+ if(!$success)
+ {
+ dao::$errors[] = $this->lang->system->errors->failToUpdateSMTP;
+ return false;
+ }
+
+ /* 2. Save SMTP account. */
+ $secretKey = helper::readKey();
+ $settingsMap->env->SMTP_PASS = openssl_encrypt($settingsMap->env->SMTP_PASS, 'DES-ECB', $secretKey);
+
+ $this->loadModel('setting');
+ $this->setting->setItem('system.common.smtp.settingsMap', json_encode($settingsMap));
+
+ return true;
+ }
+
+ /**
+ * Install global SMTP service.
+ *
+ * @param string $channel
+ * @access public
+ * @return bool
+ */
+ public function installSysSMTP($channel = 'stable')
+ {
+ $settings = fixer::input('post')->get();
+
+ $smtpApp = $this->loadModel('store')->getAppInfoByChart('cne-courier', $channel, false);
+ if(empty($smtpApp))
+ {
+ dao::$errors[] = $this->lang->system->notFoundSMTPApp;
+ return false;
+ }
+
+ $result = $this->loadModel('instance')->installSysSMTP($smtpApp, $settings, 'cne-courier', '', $channel);
+ return $result;
+ }
+
+ /**
+ * Uninstall system SMTP.
+ *
+ * @access public
+ * @return bool
+ */
+ public function uninstallSysSMTP()
+ {
+ $smtpLinked = $this->loadModel('instance')->countSMTP();
+ if($smtpLinked)
+ {
+ dao::$errors[] = $this->lang->system->errors->SMTPLinked;
+ return false;
+ }
+
+ $instanceID = $this->setting->getItem('owner=system&module=common§ion=smtp&key=instanceID');
+ $instance = $this->instance->getByID($instanceID);
+ if($instance)
+ {
+ /* 1. Uninstall QuCheng LDAP service. */
+ if(!$this->loadModel('instance')->uninstall($instance))
+ {
+ dao::$errors[] = $this->lang->system->errors->failToUninstallSMTP;
+ return false;
+ }
+
+ /* 2. Remove snippet config map from CNE. */
+ $space = $this->loadModel('space')->getSystemSpace($this->app->user->account);
+
+ $apiParams = new stdclass;
+ $apiParams->name = 'snippet-smtp-proxy';
+ $apiParams->namespace = $space->k8space;
+
+ $result = $this->loadModel('cne')->removeSnippet($apiParams);
+ if($result->code != 200)
+ {
+ dao::$errors[] = $this->lang->system->errors->failToUninstallQuChengLDAP;
+ return false;
+ }
+ }
+
+ /* 3. Delete SMTP settings in database. */
+ $this->setting->deleteItems('owner=system&module=common§ion=smtp&key=enabled');
+ $this->setting->deleteItems('owner=system&module=common§ion=smtp&key=instanceID');
+ $this->setting->deleteItems('owner=system&module=common§ion=smtp&key=snippetName');
+ $this->setting->deleteItems('owner=system&module=common§ion=smtp&key=settingsMap');
+ $this->setting->deleteItems('owner=system&module=common§ion=smtp&key=snippetSettings');
+
+ return true;
+ }
+
+ /**
+ * Get customized domain settings. *
+ * @access public
+ * @return object
+ */
+ public function getDomainSettings()
+ {
+ $settings = new stdclass;
+ $settings->customDomain = $this->setting->getItem('owner=system&module=common§ion=domain&key=customDomain');
+ $settings->https = $this->setting->getItem('owner=system&module=common§ion=domain&key=https');
+ $settings->certPem = ''; //
+ $settings->certKey = ''; //
+
+ return $settings;
+ }
+
+ /**
+ * Save customized somain settings.
+ *
+ * @access public
+ * @return void
+ */
+ public function saveDomainSettings()
+ {
+ $settings = fixer::input('post')
+ ->setDefault('customDomain', '')
+ ->setDefault('https', 'false')
+ ->setIf(is_array($this->post->https) && in_array('true', $this->post->https), 'https', 'true')
+ ->setDefault('certPem', '')
+ ->setDefault('certKey', '')
+ ->get();
+
+ $this->dao->from('system')->data($settings)
+ ->check('customDomain', 'notempty')
+ ->checkIf($settings->https == 'true', 'certPem', 'notempty')
+ ->checkIf($settings->https == 'true', 'certKey', 'notempty');
+ if(dao::isError()) return;
+
+ if(!validater::checkREG($settings->customDomain, '/^((?!-)[a-z0-9-]{1,63}(?lang->system->errors->invalidDomain;
+ return;
+ }
+
+ /* Upload Certificate to CNE. */
+ if($settings->https == 'true')
+ {
+ $cert = new stdclass;
+ $cert->name = 'tls-' . str_replace('.', '-', $settings->customDomain);
+ $cert->certificate_pem = $settings->certPem;
+ $cert->private_key_pem = $settings->certKey;
+ $certResult = $this->loadModel('cne')->uploadCert($cert);
+ if($certResult->code != 200)
+ {
+ dao::$errors[] = $certResult->message;
+ return;
+ }
+ }
+
+ $oldSettings = $this->getDomainSettings();
+ if($settings->customDomain == $oldSettings->customDomain)
+ {
+ dao::$errors[] = $this->lang->system->errors->newDomainIsSameWithOld;
+ return;
+ }
+
+ if(stripos($settings->customDomain, 'haogs.cn') !== false)
+ {
+ dao::$errors[] = $this->lang->system->errors->forbiddenOriginalDomain;
+ return;
+ }
+
+ $expiredDomain = $this->setting->getItem('owner=system&module=common§ion=domain&key=expiredDomain');
+ $expiredDomain = empty($expiredDomain ) ? array(getenv('APP_DOMAIN')) : json_decode($expiredDomain, true);
+ $expiredDomain[] = zget($settings, 'customDomain', '');
+ $this->setting->setItem('system.common.domain.expiredDomain', json_encode($expiredDomain));
+
+ $this->setting->setItem('system.common.domain.customDomain', zget($settings, 'customDomain', ''));
+ $this->setting->setItem('system.common.domain.https', zget($settings, 'https', 'false'));
+
+ $this->loadModel('instance')->updateInstancesDomain();
+
+ $this->updateMinioDomain();
+ }
+
+ /**
+ * Update minio domain.
+ *
+ * @access public
+ * @return void
+ */
+ public function updateMinioDomain()
+ {
+ $this->loadModel('cne');
+ $sysDomain = $this->cne->sysDomain();
+
+ $minioInstance = new stdclass;
+ $minioInstance->k8name = 'cne-operator';
+ $minioInstance->chart = 'cne-operator';
+ $minioInstance->spaceData = new stdclass;
+ $minioInstance->spaceData->k8space = $this->config->k8space;
+
+ $settings = new stdclass;
+ $settings->settings_map = new stdclass;
+ $settings->settings_map->minio = new stdclass;
+ $settings->settings_map->minio->ingress = new stdclass;
+ $settings->settings_map->minio->ingress->enabled = true;
+ $settings->settings_map->minio->ingress->host = 's3.' . $sysDomain;
+
+ $this->cne->updateConfig($minioInstance, $settings);
+ }
+
+ /**
+ * Get SLB settings.
+ *
+ * @access public
+ * @return object
+ */
+ public function getSLBSettings()
+ {
+ $settings = new stdclass;
+ $settings->instanceID = $this->setting->getItem('owner=system&module=common§ion=slb&key=instanceID');
+ $settings->name = $this->setting->getItem('owner=system&module=common§ion=slb&key=name');
+ $settings->ippool = '';
+
+ if($settings->instanceID)
+ {
+ $slbInstance = $this->loadModel('instance')->getByID($settings->instanceID);
+
+ $qlbInfo = $this->loadmodel('cne')->getQLBinfo($settings->name, $slbInstance->spaceData->k8space); // QLB: Qucheng load balancer.
+ if($qlbInfo) $settings->ippool = $qlbInfo->ippool;
+ }
+
+ return $settings;
+ }
+
+ /**
+ * Save SLB settings.
+ *
+ * @access public
+ * @return void
+ */
+ public function saveSLBSettings()
+ {
+ $settings = fixer::input('post')
+ ->setDefault('ippool', '')
+ ->get();
+ if(empty($settings->ippool))
+ {
+ dao::$errors[] = $this->lang->system->errors->ippoolRequired;
+ return;
+ }
+
+ $reg1Result = validater::checkREG($settings->ippool, '/^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}\/\d{1,2}$/');
+ $reg2Result = validater::checkREG($settings->ippool, '/^(((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3})-(((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3})$/');
+ if($reg1Result === false && $reg2Result === false)
+ {
+ dao::$errors[] = $this->lang->system->errors->wrongIPRange;
+ return;
+ }
+
+ $instanceID = $this->setting->getItem('owner=system&module=common§ion=slb&key=instanceID');
+ if(!$instanceID)
+ {
+ /* 1. Install SLB component metallb at first time. */
+ $slbInstance = $this->installSLBInstance();
+ if(!$slbInstance) return;
+
+ $instanceID = $slbInstance->id;
+ $this->setting->setItem('system.common.slb.instanceID', $slbInstance->id);
+ }
+
+ $instance = $this->loadModel('instance')->getByID($instanceID);
+ $status = '';
+ /* Wait 30 seconds at most for SLB instance ready. */
+ for($times = 0; $times < 10; $times++)
+ {
+ sleep(3);
+ $statusResponse = $this->loadModel('cne')->queryStatus($instance);
+ if($statusResponse->code != 200) continue;
+
+ $status = $statusResponse->data->status;
+ if($status == 'running') break;
+ }
+
+ if($status != 'running')
+ {
+ dao::$errors[] = $this->lang->system->errors->tryReinstallSLB;
+ return;
+ }
+
+ /* 2. Config SLB. */
+ $settings->name = 'qlb-quickon';
+ $settings->namespace = $this->config->k8space;
+ $success = $this->loadModel('cne')->configQLB($settings);
+ if(!$success)
+ {
+ dao::$errors[] = $this->lang->system->errors->failedToConfigSLB;
+ return;
+ }
+
+ $this->setting->setItem('system.common.slb.name', zget($settings, 'name', ''));
+ }
+
+ /**
+ * Install SLB instance.
+ *
+ * @access private
+ * @return object|null
+ */
+ private function installSLBInstance()
+ {
+ $channel = $this->app->session->cloudChannel ? $this->app->session->cloudChannel : $this->config->cloud->api->channel;
+
+ $slbApp = new stdclass;
+ $slbApp->name = 'metallb';
+ $slbApp->alias = 'metallb';
+ $slbApp->desc = 'metallb';
+ $slbApp->chart = 'metallb';
+ $slbApp->app_version = '';
+ $slbApp->version = '';
+ $slbApp->id = '';
+ $slbApp->logo = '';
+
+ $slbInstance = $this->loadModel('instance')->installSysSLB($slbApp, 'cne-lb', $channel);
+ if(!$slbInstance)
+ {
+ dao::$errors[] = $this->lang->system->errors->failedToInstallSLBComponent;
+ return;
+ }
+
+ return $slbInstance;
+ }
+
+ /**
+ * Print SMTP buttons.
+ *
+ * @param objevt $smtpInstance
+ * @access public
+ * @return string
+ */
+ public function printSMTPButtons($smtpInstance)
+ {
+ $this->loadModel('instance');
+ $this->app->loadLang('instance');
+
+ $buttonHtml = '';
+
+ $disableStart = !$this->instance->canDo('start', $smtpInstance);
+ $buttonHtml .= html::commonButton($this->lang->instance->start, "instance-id='{$smtpInstance->id}' title='{$this->lang->instance->start}'" . ($disableStart ? ' disabled ' : ''), "btn-start btn label label-outline label-primary label-lg");
+
+ $title = $this->lang->instance->stop;
+ $toolTips = '';
+ $count = $this->instance->countSMTP();
+ if($count)
+ {
+ $title = sprintf($this->lang->system->notices->smtpUsed, $count);
+ $toolTips = "data-toggle='tooltip' data-placement='bottom' runat='server'";
+ }
+
+ $buttonHtml .= "
";
+ $buttonHtml .= html::a(inLink('editSMTP'), $this->lang->system->SMTP->edit, '', "title='{$title}' class='btn-edit btn label label-outline label-primary label-lg'");
+ $buttonHtml .= "";
+
+ $disableStop = $count > 0 || !$this->instance->canDo('stop', $smtpInstance);
+ $buttonHtml .= "
";
+ $buttonHtml .= html::commonButton($this->lang->instance->stop, "instance-id='{$smtpInstance->id}' title='{$title}'" . ($disableStop ? ' disabled ' : ''), 'btn-stop btn label label-outline label-danger label-lg');
+ $buttonHtml .= "";
+
+ echo $buttonHtml;
+ }
+
+ public function getK8sTag()
+ {
+ $serviceAccount = '/var/run/secrets/kubernetes.io/serviceaccount';
+ if(!is_dir($serviceAccount)) return "Fail: Please run script in k8s.";
+
+ $nameSpace = file_get_contents($serviceAccount . '/namespace');
+
+ $apiServer = empty($_POST['apiServer']) ? 'https://kubernetes.default.svc' : $_POST['apiServer'];
+ $token = file_get_contents($serviceAccount . '/token');
+ $cacert = $serviceAccount . '/ca.crt';
+
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, "$apiServer/api/v1/namespaces/{$nameSpace}/serviceaccounts/default");
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_CAINFO, $cacert);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: Bearer $token"));
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLINFO_HEADER_OUT, true);
+ $response = curl_exec($curl);
+ $errors = curl_error($curl);
+ curl_close($curl);
+
+ if($errors) return "Fail: " . $errors;
+
+ $serviceAccounts = json_decode($response);
+ if(empty($serviceAccounts->metadata->uid))
+ {
+ if($serviceAccounts->code == '403') return $this->lang->misc->k8s->repairNotice;
+ return "Fail: " . $response;
+ }
+
+ $infos = array();
+ $infos['apiServer'] = $apiServer;
+ $infos['nameSpace'] = $nameSpace;
+ $infos['uid'] = '';
+ if(isset($serviceAccounts->metadata->uid)) $infos['uid'] = $serviceAccounts->metadata->uid;
+
+ if(empty($infos['uid'])) return "Fail: Can not find k8s uid information";
+
+ $md5 = md5($this->config->global->sn);
+ $systemDebug = $this->config->debug;
+ $this->config->debug = false;
+ $encrypted = @openssl_encrypt(json_encode($infos), 'DES-CBC', substr($md5, 0, 8));
+ $this->config->debug = $systemDebug;
+ return $encrypted;
+ }
+
+ /**
+ * Backup SQL.
+ *
+ * @param string $backupFile
+ * @access public
+ * @return object
+ */
+ public function backSQL($backupFile, $backupType = 'manual')
+ {
+ $zdb = $this->app->loadClass('zdb');
+ $dumpStatus = $zdb->dump($backupFile);
+ if($dumpStatus->result === true) $this->processSQLSummary($backupFile, $backupType);
+ return $dumpStatus;
+ }
+
+ /**
+ * Restore SQL.
+ *
+ * @param string $backupFile
+ * @access public
+ * @return object
+ */
+ public function restoreSQL($backupFile)
+ {
+ $zdb = $this->app->loadClass('zdb');
+ $nosafe = strpos($this->config->backup->setting, 'nosafe') !== false;
+
+ $backupDir = dirname($backupFile);
+ $fileName = date('YmdHis') . mt_rand(0, 9);
+ $backFileName = "{$backupDir}/{$fileName}.sql";
+ if(!$nosafe) $backFileName .= '.php';
+
+ $result = $this->backSQL($backFileName, 'restore');
+ if($result->result and !$nosafe) $this->addFileHeader($backFileName);
+
+ $allTables = $zdb->getAllTables();
+ foreach($allTables as $tableName => $tableType)
+ {
+ try
+ {
+ $this->dbh->query("DROP $tableType IF EXISTS `$tableName`");
+ }
+ catch(PDOException $e){}
+ }
+
+ $importResult = $zdb->import($backupFile);
+
+ if($importResult && $importResult->result)
+ {
+ $this->loadModel('instance')->restoreInstanceList();
+ $this->processRestoreSummary('sql', 'done');
+ }
+
+ return $importResult;
+ }
+
+ /**
+ * Restore File.
+ *
+ * @param string $backupFile
+ * @access public
+ * @return object
+ */
+ public function restoreFile($backupFile)
+ {
+ $return = new stdclass();
+ $return->result = true;
+ $return->error = '';
+
+ if(is_file($backupFile))
+ {
+ $oldDir = getcwd();
+ chdir($this->app->getTmpRoot());
+ $this->app->loadClass('pclzip', true);
+ $zip = new pclzip($backupFile);
+ if($zip->extract(PCLZIP_OPT_PATH, $this->app->getAppRoot() . 'www/data/', PCLZIP_OPT_TEMP_FILE_ON) == 0)
+ {
+ $return->result = false;
+ $return->error = $zip->errorInfo();
+ }
+ chdir($oldDir);
+ }
+ elseif(is_dir($backupFile))
+ {
+ $zfile = $this->app->loadClass('zfile');
+ $zfile->copyDir($backupFile, $this->app->getAppRoot() . 'www/data/', $showDetails = false);
+ }
+
+ $this->processRestoreSummary('file', 'done');
+
+ return $return;
+ }
+
+ /**
+ * Get backup account and backup type.
+ *
+ * @param string $file
+ * @access public
+ * @return array
+ */
+ public function getSQLSummary($file)
+ {
+ $summaryFile = $this->loadModel('backup')->getBackupPath() . DS . 'summary';
+ $sqlSummary = json_decode(file_get_contents($summaryFile), true);
+ return isset($sqlSummary[basename($file)]) ? $sqlSummary[basename($file)] : array();
+ }
+
+ /**
+ * Process restore summay.
+ *
+ * @param string $restoreType
+ * @param string $status
+ * @param string $action
+ * @access public
+ * @return bool
+ */
+ public function processRestoreSummary($restoreType = 'sql', $status = 'done', $action = 'add')
+ {
+ $summaryFile = $this->loadModel('backup')->getBackupPath() . DS . 'restoreSummary';
+ if(!file_exists($summaryFile) and !touch($summaryFile)) return false;
+
+ $summary = json_decode(file_get_contents($summaryFile), true);
+ if(empty($summary)) $summary = array();
+
+ if($action == 'add')
+ {
+ $summary[$restoreType] = $status;
+ }
+ else
+ {
+ $summary = array();
+ }
+ if(file_put_contents($summaryFile, json_encode($summary))) return true;
+ return false;
+
+ }
+
+ /**
+ * Save backup account and backup type.
+ *
+ * @param string $file
+ * @param string $type
+ * @param string $action
+ * @access public
+ * @return bool
+ */
+ public function processSQLSummary($file, $type = 'manual', $action = 'add')
+ {
+ $backupPath = dirname($file);
+ $fileName = basename($file);
+
+ $summaryFile = $backupPath . DS . 'summary';
+ if(!file_exists($summaryFile) and !touch($summaryFile)) return false;
+
+ $summary = json_decode(file_get_contents($summaryFile), true);
+ if(empty($summary)) $summary = array();
+
+ if($action == 'add')
+ {
+ $summary[$fileName]['account'] = $this->app->user->account == 'guest' ? '' : $this->app->user->account;
+ $summary[$fileName]['backupType'] = $type;
+ }
+ else
+ {
+ unset($summary[$fileName]);
+ }
+
+ if(file_put_contents($summaryFile, json_encode($summary))) return true;
+ return false;
+ }
+
+ /**
+ * Check upgrade process is overtime (5 miniutes) or not.
+ *
+ * @access public
+ * @return mixed
+ */
+ public function isGradeOvertime()
+ {
+ $upgradedAt = $this->loadModel('setting')->getItem('owner=system&module=backup§ion=global&key=upgradedAt');
+
+ return (time() - intval($upgradedAt)) > 300;
+ }
+
+}
diff --git a/module/system/ui/browsebackup.html.php b/module/system/ui/browsebackup.html.php
new file mode 100644
index 0000000000..ab7cf43811
--- /dev/null
+++ b/module/system/ui/browsebackup.html.php
@@ -0,0 +1,37 @@
+
+ * @package system
+ * @link https://www.zentao.net
+ */
+namespace zin;
+
+foreach($backups as $backup)
+{
+ $backup->backupPerson = isset($backup->sqlSummary['account']) ? $backup->sqlSummary['account'] : '';
+ if(empty($backup->sqlSummary['backupType'])) $backup->sqlSummary['backupType'] = '';
+ $backup->type = $backup->sqlSummary['backupType'];
+}
+$backups = initTableData($backups, $config->system->dtable->backup->fieldList, $this->system);
+
+panel
+(
+ set::size('lg'),
+ set::title($lang->system->backup->systemInfo),
+ div
+ (
+ setStyle('width', '66.6%'),
+ dtable
+ (
+ set::cols($config->system->dtable->backup->fieldList),
+ set::data($backups),
+ ),
+ ),
+);
+
+render();
+
diff --git a/module/system/ui/dblist.html.php b/module/system/ui/dblist.html.php
new file mode 100644
index 0000000000..173f2fa61d
--- /dev/null
+++ b/module/system/ui/dblist.html.php
@@ -0,0 +1,34 @@
+
+ * @package system
+ * @link https://www.zentao.net
+ */
+namespace zin;
+
+$config->system->dtable->dbList->fieldList['actions']['list']['management']['url'] = 'javascript:manageDb("{name}", "{db_type}", "{namespace}")';
+
+$dbList = initTableData($dbList, $config->system->dtable->dbList->fieldList, $this->system);
+
+panel
+(
+ set::size('lg'),
+ set::title($lang->system->dbList),
+ div
+ (
+ setStyle('width', '66.6%'),
+ dtable
+ (
+ set::cols($config->system->dtable->dbList->fieldList),
+ set::data($dbList),
+ set::onRenderCell(jsRaw('window.renderDbList'))
+ ),
+ ),
+);
+
+render();
+
diff --git a/module/system/ui/domainview.html.php b/module/system/ui/domainview.html.php
new file mode 100644
index 0000000000..c4f5ed8c74
--- /dev/null
+++ b/module/system/ui/domainview.html.php
@@ -0,0 +1,43 @@
+
+ * @package system
+ * @link https://www.zentao.net
+ */
+namespace zin;
+
+$expiredDate = !empty($cert) ? zget($cert, 'expiredDate', '') : '';
+
+panel
+(
+ set::size('lg'),
+ set::title($lang->system->domain->common),
+ to::headingActions
+ (
+ btn
+ (
+ setClass('primary'),
+ $lang->system->domain->editDomain,
+ set::url($this->createLink('system', 'editDomain')),
+ ),
+ ),
+ tableData
+ (
+ item
+ (
+ set::name($lang->system->domain->currentDomain),
+ zget($domainSettings, 'customDomain', ''),
+ ),
+ item
+ (
+ set::name($lang->system->domain->expiredDate),
+ $expiredDate
+ ),
+ ),
+);
+
+render();
diff --git a/module/system/ui/editdomain.html.php b/module/system/ui/editdomain.html.php
new file mode 100644
index 0000000000..09dbf9d75f
--- /dev/null
+++ b/module/system/ui/editdomain.html.php
@@ -0,0 +1,94 @@
+
+ * @package system
+ * @link https://www.zentao.net
+ */
+namespace zin;
+
+formPanel
+(
+ set::title($lang->system->domain->config),
+ set::id('domainForm'),
+ formRow
+ (
+ formGroup
+ (
+ set::label($lang->system->domain->oldDomain),
+ set::control('static'),
+ set::value($this->loadModel('cne')->sysDomain()),
+ h::span
+ (
+ setClass('tips-git leading-8 ml-2 text-danger'),
+ $lang->system->domain->notReuseOldDomain,
+ ),
+ ),
+ ),
+ formGroup
+ (
+ set::label($lang->system->domain->newDomain),
+ set::name('customDomain'),
+ set::value(zget($domainSettings, 'customDomain', '')),
+ set::required(true),
+ h::span
+ (
+ setClass('tips-git leading-8 ml-2'),
+ $lang->system->domain->setDNS,
+ ),
+ h::a
+ (
+ setClass('leading-8'),
+ set::target('_blank'),
+ set::href('https://www.qucheng.com/book/Installation-manual/47.html'),
+ $lang->system->domain->dnsHelperLink
+ ),
+ ),
+ formGroup
+ (
+ set::label(''),
+ set::name('https[]'),
+ set::control(array('type' => 'checkbox', 'text' => $lang->system->domain->uploadCert, 'value' => 'true')),
+ on::change('onHttpsChange'),
+ ),
+ formGroup
+ (
+ setClass('cert hidden'),
+ set::label($lang->system->certPem),
+ set::name('certPem'),
+ set::control('textarea'),
+ set::value(zget($domainSettings, 'certPem', '')),
+ ),
+ formGroup
+ (
+ setClass('cert hidden'),
+ set::label($lang->system->certKey),
+ set::name('certPem'),
+ set::control('textarea'),
+ set::value(zget($domainSettings, 'certKey', '')),
+ ),
+ formRow
+ (
+ formGroup
+ (
+ setClass('cert hidden'),
+ set::label(''),
+ btn
+ (
+ $lang->system->verify,
+ set::id('validateCertBtn'),
+ on::click('checkCert'),
+ ),
+ h::span
+ (
+ setClass('tips-git leading-8 ml-2'),
+ set::id('validateMsg'),
+ ),
+ )
+ ),
+);
+render();
+
diff --git a/module/system/ui/index.html.php b/module/system/ui/index.html.php
new file mode 100644
index 0000000000..4373ed59e1
--- /dev/null
+++ b/module/system/ui/index.html.php
@@ -0,0 +1,14 @@
+
+ * @package system
+ * @link https://www.zentao.net
+ */
+namespace zin;
+
+
+render();
diff --git a/module/system/ui/ossview.html.php b/module/system/ui/ossview.html.php
new file mode 100644
index 0000000000..5326e74bb0
--- /dev/null
+++ b/module/system/ui/ossview.html.php
@@ -0,0 +1,85 @@
+
+ * @package system
+ * @link https://www.zentao.net
+ */
+namespace zin;
+
+jsVar('copySuccess', $lang->system->copySuccess);
+
+$apiURL = !empty($ossDomain->extra_hosts) ? zget($ossDomain->extra_hosts, 'api', '') : '';
+
+panel
+(
+ set::size('lg'),
+ set::title($lang->system->oss->common),
+ tableData
+ (
+ item
+ (
+ set::name($lang->system->oss->appURL),
+ a
+ (
+ set::id('ossVisitUrl'),
+ set::target('_blank'),
+ $lang->system->visit,
+ ),
+ ),
+ item
+ (
+ set::name($lang->system->oss->user),
+ span
+ (
+ set::id('ossAdmin'),
+ ),
+ ),
+ item
+ (
+ set::name($lang->system->oss->password),
+ h::input
+ (
+ setClass('hidden'),
+ set::id('ossPassword'),
+ set::value($ossAccount->password),
+ ),
+ btn
+ (
+ $lang->system->copy,
+ on::click('copyPassBtn'),
+ )
+ ),
+ item
+ (
+ set::name($lang->system->oss->apiURL),
+ $apiURL,
+ ),
+ item
+ (
+ set::name($lang->system->oss->accessKey),
+ $ossAccount->username,
+ ),
+ item
+ (
+ set::name($lang->system->oss->secretKey),
+ h::input
+ (
+ setClass('hidden'),
+ set::id('ossSK'),
+ set::value($ossAccount->password),
+ ),
+ btn
+ (
+ $lang->system->copy,
+ on::click('copySK'),
+ )
+ ),
+ ),
+);
+
+render();
+
diff --git a/module/system/view/dashboard.html.php b/module/system/view/dashboard.html.php
new file mode 100644
index 0000000000..0db1a8d76e
--- /dev/null
+++ b/module/system/view/dashboard.html.php
@@ -0,0 +1,20 @@
+
+ * @package my
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/system/view/dblist.html.php b/module/system/view/dblist.html.php
new file mode 100644
index 0000000000..f5697c2293
--- /dev/null
+++ b/module/system/view/dblist.html.php
@@ -0,0 +1,43 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+
+ createLink('system', 'index'), " " . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->dbList;?>
+
+
+
+ | system->dbName;?> |
+ system->dbType;?> |
+ system->dbStatus;?> |
+ system->action?> |
+
+
+
+ | name;?> |
+ db_type?> |
+ instance->statusList, $db->status);?> |
+ system->printDBAction($db);?> |
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
+
diff --git a/module/system/view/domainview.html.php b/module/system/view/domainview.html.php
new file mode 100644
index 0000000000..dbf06aa211
--- /dev/null
+++ b/module/system/view/domainview.html.php
@@ -0,0 +1,43 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->domain->common;?>
+
+ inLink('editDomain'), $lang->system->domain->editDomain, '', "class='btn btn-primary'");?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/system/view/editdomain.html.php b/module/system/view/editdomain.html.php
new file mode 100644
index 0000000000..03ad29d67c
--- /dev/null
+++ b/module/system/view/editdomain.html.php
@@ -0,0 +1,97 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+system->errors);?>
+system->notices);?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->domain->config;?>
+
+
+
+
+
+
+
+
+
system->domain->updateInstancesDomain;?>
+
+ system->domain->updating;?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
+
diff --git a/module/system/view/editldap.html.php b/module/system/view/editldap.html.php
new file mode 100644
index 0000000000..c3c0b16bcd
--- /dev/null
+++ b/module/system/view/editldap.html.php
@@ -0,0 +1,32 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+loadModel('instance')->countLDAP();?>
+system->errors);?>
+system->notices);?>
+
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->ldapManagement;?>
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
+
diff --git a/module/system/view/editslb.html.php b/module/system/view/editslb.html.php
new file mode 100644
index 0000000000..b31992d53d
--- /dev/null
+++ b/module/system/view/editslb.html.php
@@ -0,0 +1,58 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+system->errors);?>
+system->notices);?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->SLB->config;?>
+
+
+
+
+
+
+
+
+
system->SLB->installing;?>
+
+ system->SLB->leftSeconds;?>
+
+ system->SLB->second;?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
+
diff --git a/module/system/view/editsmtp.html.php b/module/system/view/editsmtp.html.php
new file mode 100644
index 0000000000..edd8c1be2e
--- /dev/null
+++ b/module/system/view/editsmtp.html.php
@@ -0,0 +1,32 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+loadModel('instance')->countSMTP();?>
+system->errors);?>
+system->notices);?>
+
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->SMTP->editSMTP;?>
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
+
diff --git a/module/system/view/index.html.php b/module/system/view/index.html.php
new file mode 100644
index 0000000000..a76bff722e
--- /dev/null
+++ b/module/system/view/index.html.php
@@ -0,0 +1,76 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+
+
+
+
+
+
+
+
+
+
+ edition == 'biz'):?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/system/view/installldap.html.php b/module/system/view/installldap.html.php
new file mode 100644
index 0000000000..33272e3546
--- /dev/null
+++ b/module/system/view/installldap.html.php
@@ -0,0 +1,31 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+loadModel('instance')->countLDAP();?>
+system->errors);?>
+system->notices);?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->installLDAP;?>
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
+
diff --git a/module/system/view/installsmtp.html.php b/module/system/view/installsmtp.html.php
new file mode 100644
index 0000000000..3fc433875f
--- /dev/null
+++ b/module/system/view/installsmtp.html.php
@@ -0,0 +1,31 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+loadModel('instance')->countSMTP();?>
+system->errors);?>
+system->notices);?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->SMTP->install;?>
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
+
diff --git a/module/system/view/instancesblock.html.php b/module/system/view/instancesblock.html.php
new file mode 100644
index 0000000000..eb6703a917
--- /dev/null
+++ b/module/system/view/instancesblock.html.php
@@ -0,0 +1,43 @@
+app->loadLang('instance');?>
+
+
+
instance->runningService;?>
+
+
+
+
+
+
+ | instance->name;?> |
+ instance->version;?> |
+ instance->status?> |
+ instance->cpu;?> |
+ instance->mem;?> |
+
+
+
+
+
+ | createLink('instance', 'view', "id=$instance->id"), $instance->name);?> |
+ appVersion;?> |
+
+ instance->printStatus($instance, false);?>
+
+
+ instance->leftTime;?>
+ 00:00
+
+
+ |
+ id);?>
+ instance->printCpuUsage($instance, $metrics->cpu);?> |
+ instance->printMemUsage($instance, $metrics->memory);?> |
+
+
+
+
+
+
+
diff --git a/module/system/view/ldapform.html.php b/module/system/view/ldapform.html.php
new file mode 100644
index 0000000000..9738a56434
--- /dev/null
+++ b/module/system/view/ldapform.html.php
@@ -0,0 +1,98 @@
+
+
+
+
+
+
system->LDAP->updateLDAP;?>
+
+
+
+
+
diff --git a/module/system/view/ldapview.html.php b/module/system/view/ldapview.html.php
new file mode 100644
index 0000000000..048c902100
--- /dev/null
+++ b/module/system/view/ldapview.html.php
@@ -0,0 +1,132 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+instance->notices);?>
+
+system->copySuccess);?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->LDAP->info;?>
+
+ system->printEditLDAPBtn();?>
+
+ system->printLDAPButtons($ldapInstance);?>
+
+ system->visit, '', "disabled class='btn label label-outline label-primary label-lg'");?>
+ system->close, '', "disabled class='btn label label-outline label-primary label-lg'");?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
system->LDAP->accountInfo;?>
+
system->LDAP->account;?>:
+
+ system->LDAP->password;?>:
+ system->copy, "id='copyPassBtn'", "btn bnt-link");?>
+
+
+
+ cancel, "data-dismiss='modal'", 'btn btn-wide');?>
+ system->visit, '_blank', "id='ldapVisitUrl' class='btn btn-primary btn-wide'");?>
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/system/view/ossview.html.php b/module/system/view/ossview.html.php
new file mode 100644
index 0000000000..f0a2822c97
--- /dev/null
+++ b/module/system/view/ossview.html.php
@@ -0,0 +1,69 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+system->copySuccess);?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->oss->common;?>
+
+ system->oss->manage, "id='ossManage'", "btn label label-outline label-primary label-lg");?>
+
+
+
+
+
+
+
+
+
+
+
system->accountInfo;?>
+
system->oss->user;?>:
+
+ system->oss->password;?>:
+ system->copy, "id='copyPassBtn'", 'btn btn-link');?>
+
+
+ cancel, "data-dismiss='modal'", 'btn btn-wide');?>
+ system->visit, '_blank', "id='ossVisitUrl' class='btn btn-primary btn-wide'");?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/system/view/overviewblock.html.php b/module/system/view/overviewblock.html.php
new file mode 100644
index 0000000000..5b868bb7b6
--- /dev/null
+++ b/module/system/view/overviewblock.html.php
@@ -0,0 +1,32 @@
+app->loadLang('cne');?>
+
+
+
+
system->cneStatus;?>
+
lang->CNE->statusIcons, $cneMetrics->status, $this->lang->CNE->statusIcons['unknown']);?>
+
CNE->statusList, $cneMetrics->status, $lang->CNE->statusList['unknown']);?>
+
+
+
system->cneStatistic;?>
+
+
+
system->nodeQuantity;?>
+
node_count;?>
+
+
+
system->serviceQuantity;?>
+
instance->totalServices();?>
+
+
+
system->cpuUsage;?>
+ cne->printCpuUsage($cneMetrics->metrics->cpu);?>
+
+
+
system->memUsage;?>
+ cne->printMemUsage($cneMetrics->metrics->memory);?>
+
+
+
+
+
+
diff --git a/module/system/view/slbview.html.php b/module/system/view/slbview.html.php
new file mode 100644
index 0000000000..28da2501da
--- /dev/null
+++ b/module/system/view/slbview.html.php
@@ -0,0 +1,38 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->SLB->common;?>
+
+ inLink('editSLB'), $lang->system->SLB->edit, '', "class='btn btn-primary'");?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/module/system/view/smtpform.html.php b/module/system/view/smtpform.html.php
new file mode 100644
index 0000000000..a95ed09ae0
--- /dev/null
+++ b/module/system/view/smtpform.html.php
@@ -0,0 +1,36 @@
+
diff --git a/module/system/view/smtpview.html.php b/module/system/view/smtpview.html.php
new file mode 100644
index 0000000000..1efc99e40e
--- /dev/null
+++ b/module/system/view/smtpview.html.php
@@ -0,0 +1,60 @@
+
+ * @package system
+ * @version $Id$
+ * @link https://www.qucheng.com
+ */
+?>
+app->getModuleRoot() . '/common/view/header.html.php';?>
+instance->notices);?>
+id));?>
+
+
+ createLink('system', 'index'), "" . $lang->goback, '', "class='btn btn-secondary'");?>
+
+
+
+
+
+
system->SMTP->common;?>
+
+ system->printSMTPButtons($smtpInstance);?>
+
+
+
+
+
+app->getModuleRoot() . '/common/view/footer.html.php';?>
diff --git a/www/static/svg/admin-platform.svg b/www/static/svg/admin-platform.svg
new file mode 100644
index 0000000000..13f7ff96b5
--- /dev/null
+++ b/www/static/svg/admin-platform.svg
@@ -0,0 +1,4 @@
+