diff --git a/assets/OsSelectionAsset.php b/assets/OsSelectionAsset.php
new file mode 100644
index 00000000..6a1e0013
--- /dev/null
+++ b/assets/OsSelectionAsset.php
@@ -0,0 +1,30 @@
+ 'name'];
+
+ /** @inheritdoc */
+ public $_filter = ['client' => 'client'];
+
+ /** @inheritdoc */
+ function getConfig ($config = []) {
+ $config = ArrayHelper::merge([
+ 'clearWhen' => ['client'],
+ 'affects' => [
+ 'client' => 'client',
+ 'seller' => 'seller'
+ ]
+ ], $config);
+
+ return parent::getConfig($config);
+ }
+}
\ No newline at end of file
diff --git a/assets/css/OsSelection.css b/assets/css/OsSelection.css
new file mode 100644
index 00000000..579eb91e
--- /dev/null
+++ b/assets/css/OsSelection.css
@@ -0,0 +1,3 @@
+label.disabled {
+ color: darkgrey;
+}
\ No newline at end of file
diff --git a/assets/js/OsSelection.js b/assets/js/OsSelection.js
new file mode 100644
index 00000000..480d1898
--- /dev/null
+++ b/assets/js/OsSelection.js
@@ -0,0 +1,142 @@
+(function ($, window, document, undefined) {
+ var pluginName = "osSelector",
+ defaults = {
+ panelInput: '.reinstall-panel',
+ osimageInput: '.reinstall-osimage'
+ };
+
+ function Plugin(element, options) {
+ this.element = $(element);
+ this.items = {};
+ this.oslist = undefined;
+ this.softlist = undefined;
+ this.osparams = options.osparams;
+ this.settings = $.extend({}, defaults, options);
+ this._defaults = defaults;
+ this._name = pluginName;
+ this.init();
+ }
+
+ Plugin.prototype = {
+ init: function () {
+ this.oslist = this.element.find('.os-list');
+ this.softlist = this.element.find('.soft-list');
+ this.bindListeners();
+ this.afterInit();
+ this.enableInfoPopover();
+ },
+ bindListeners: function () {
+ var _this = this;
+
+ this.softlist.find('input').on('change', function () {
+ var soft = $(this).val();
+ var panel = $(this).data('panel');
+ var os = _this.oslist.find('input.radio:checked').first().val();
+ $(_this.settings.osimageInput).val(_this.osparams[os]['panel'][panel]['softpack'][soft]['osimage']);
+ if (panel != 'no') {
+ $(_this.settings.panelInput).removeAttr('disabled');
+ $(_this.settings.panelInput).val(panel);
+ } else $(_this.settings.panelInput).attr('disabled', 'disabled');
+ });
+
+ $(this.oslist).on('change', function () {
+ _this.update();
+ });
+ },
+ update: function () {
+ var _this = this;
+
+ if (this.oslist.find('.radio:enabled:checked').length < 1) this.oslist.find('.radio:enabled').first().prop('checked', true);
+ this.oslist.find('.radio:enabled').each(function () {
+ if ($(this).prop('checked')) {
+ _this.softlist.find('input').attr('disabled', 'disabled');
+ _this.softlist.find('input').closest('label').addClass('disabled');
+ _this.softlist.find('.softinfo-bttn').hide();
+
+ var osName = $(this).val();
+ for (var key in _this.osparams) {
+ if (osName == key) {
+ for (var panel in _this.osparams[key]['panel']) {
+ for (var soft in _this.osparams[key]['panel'][panel]['softpack']) {
+ var selectable = _this.osparams[key]['panel'][panel]['softpack'][soft];
+ var $inputs = _this.softlist.filter('[data-panel="' + panel + '"]');
+ if (selectable) {
+ var $selectable_inputs = $inputs.find('input[value="' + soft + '"]');
+ var soft_desc = selectable['html_desc'];
+
+ $selectable_inputs.removeAttr('disabled');
+ $selectable_inputs.closest('label').removeClass('disabled');
+ if (soft_desc) {
+ $selectable_inputs.closest('label').find('.soft-desc').html(soft_desc);
+ $selectable_inputs.closest('label').find('.softinfo-bttn').show();
+ }
+ }
+ }
+ }
+ }
+ }
+ if (_this.softlist.find('input:enabled:checked').length == 0) {
+ _this.softlist.find('input:enabled').first().attr('checked', 'checked');
+ }
+ _this.softlist.find('input:checked').trigger('change').trigger('click');
+ }
+ });
+ },
+ afterInit: function () {
+ this.oslist.find('input:enabled').first().attr('checked', 'checked').trigger('change');
+ },
+ enableInfoPopover: function () {
+ var counter;
+
+ $('.softinfo-bttn').popover({
+ trigger: 'manual',
+ html: true,
+ title: function () {
+ return $(this).parent().find('.panel_soft').val();
+ },
+ content: function () {
+ return $(this).parent().find('.soft-desc').html();
+ },
+ container: 'body',
+ placement: 'auto'
+ }).on("mouseenter", function () {
+ var _this = this;
+ // clear the counter
+ clearTimeout(counter);
+ // Close all other Popovers
+ $('.softinfo-bttn').not(_this).popover('hide');
+
+ // start new timeout to show popover
+ counter = setTimeout(function () {
+ if ($(_this).is(':hover')) {
+ $(_this).popover("show");
+ }
+ $(".popover").on("mouseleave", function () {
+ $('.thumbcontainer').popover('hide');
+ });
+ }, 400);
+
+ }).on("mouseleave", function () {
+ var _this = this;
+
+ setTimeout(function () {
+ if (!$(".popover:hover").length) {
+ if (!$(_this).is(':hover')) {
+ $(_this).popover('hide');
+ }
+ }
+ }, 200);
+ });
+ }
+
+ };
+
+ $.fn[pluginName] = function (options) {
+ this.each(function () {
+ if (!$.data(this, "plugin_" + pluginName)) {
+ $.data(this, "plugin_" + pluginName, new Plugin(this, options));
+ }
+ });
+ return this;
+ };
+})(jQuery, window, document);
\ No newline at end of file
diff --git a/controllers/ServerController.php b/controllers/ServerController.php
index e000b20c..64c8d421 100644
--- a/controllers/ServerController.php
+++ b/controllers/ServerController.php
@@ -7,7 +7,299 @@
namespace hipanel\modules\server\controllers;
-class ServerController extends \hipanel\base\CrudController
+use hipanel\actions\RequestStateAction;
+use hipanel\base\CrudController;
+use hipanel\models\Ref;
+use hipanel\modules\server\models\Osimage;
+use hipanel\modules\server\models\Server;
+use hiqdev\hiar\HiResException;
+use yii\helpers\ArrayHelper;
+use yii\web\NotFoundHttpException;
+
+class ServerController extends CrudController
{
+ public function behaviors () {
+ return [];
+ }
+
+ public function actions () {
+ return array_merge(parent::actions(), [
+ 'requests-state' => [
+ 'class' => RequestStateAction::className(),
+ 'model' => Server::className()
+ ]
+ ]);
+ }
+
+ public function actionIndex () {
+ return parent::actionIndex([
+ 'osimages' => $this->getOsimages(),
+ 'states' => $this->getStates()
+ ]);
+ }
+
+ public function actionView ($id) {
+ $model = $this->findModel($id);
+ $model->vnc = $this->getVNCInfo($model);
+
+ $osimages = $this->getOsimages();
+ $osimageslivecd = $this->getOsimagesLiveCd();
+ $grouped_osimages = $this->getGroupedOsimages($osimages);
+ $panels = $this->getPanelTypes();
+
+ return $this->render('view', compact('model', 'osimages', 'osimageslivecd', 'grouped_osimages', 'panels'));
+ }
+
+
+ /**
+ * Enables VNC on the server
+ *
+ * @param $id
+ * @return string
+ * @throws NotFoundHttpException
+ * @throws \yii\base\NotSupportedException
+ */
+ public function actionEnableVnc ($id) {
+ $model = $this->findModel($id);
+ $model->checkOperable();
+ $model->vnc = $this->getVNCInfo($model, true);
+
+ return $this->actionView($id);
+ }
+
+ /**
+ * Gets info of VNC on the server
+ *
+ * @param \frontend\modules\server\models\Server $model
+ * @param bool $enable
+ *
+ * @return array
+ * @throws HiResException
+ */
+ private function getVNCInfo ($model, $enable = false) {
+ $vnc['endTime'] = strtotime('+8 hours', strtotime($model->statuses['serverEnableVNC']));
+ if (($vnc['endTime'] > time() || $enable) && $model->isOperable()) {
+ $vnc['enabled'] = true;
+ $vnc = ArrayHelper::merge($vnc, Server::perform('EnableVNC', ['id' => $model->id]));
+ }
+
+ return $vnc;
+ }
+
+
+
+
+// public function actionReinstall ($id) {
+// return $this->operate([
+// 'id' => $id,
+// 'params' => function ($model) {
+// return [
+// 'id' => $model->id,
+// 'osimage' => \Yii::$app->request->post('osimage'),
+// 'panel' => \Yii::$app->request->post('panel')
+// ];
+// },
+// 'action' => 'Resetup',
+// 'errorMessage' => 'Error while server re-intalling',
+// 'successMessage' => 'Server reinstalling task has been successfully added to queue',
+// ]);
+// }
+// public function actionReboot ($id) {
+// return $this->operate([
+// 'id' => $id,
+// 'action' => 'Reboot',
+// 'errorMessage' => 'Error while rebooting',
+// 'successMessage' => 'Reboot task has been successfully added to queue',
+// ]);
+// }
+//
+// public function actionReset ($id) {
+// return $this->operate([
+// 'id' => $id,
+// 'action' => 'Reset',
+// 'errorMessage' => 'Error while resetting',
+// 'successMessage' => 'Reset task has been successfully added to queue',
+// ]);
+// }
+//
+// public function actionShutdown ($id) {
+// return $this->operate([
+// 'id' => $id,
+// 'action' => 'Shutdown',
+// 'errorMessage' => 'Error while shutting down',
+// 'successMessage' => 'Shutdown task has been successfully added to queue',
+// ]);
+// }
+//
+// public function actionPowerOff ($id) {
+// return $this->operate([
+// 'id' => $id,
+// 'action' => 'PowerOff',
+// 'errorMessage' => 'Error while turning power off',
+// 'successMessage' => 'Power off task has been successfully added to queue',
+// ]);
+// }
+//
+// public function actionPowerOn ($id) {
+// return $this->operate([
+// 'id' => $id,
+// 'action' => 'PowerOn',
+// 'errorMessage' => 'Error while turning power on',
+// 'successMessage' => 'Power on task has been successfully added to queue',
+// ]);
+// }
+//
+// public function actionBootLive ($id, $osimage) {
+// return $this->operate([
+// 'id' => $id,
+// 'params' => function ($model) use ($osimage) {
+// return ['id' => $model->id, 'osimage' => $osimage];
+// },
+// 'action' => 'BootLive',
+// 'errorMessage' => 'Error while booting live CD',
+// 'successMessage' => 'Live CD booting task has been successfully added to queue',
+// ]);
+// }
+//
+// public function actionRegenRootPassword ($id) {
+// return $this->operate([
+// 'id' => $id,
+// 'action' => 'RegenRootPassword',
+// 'errorMessage' => 'Error while password regeneration',
+// 'successMessage' => 'Password regenerating task has been successfully added to queue',
+// ]);
+// }
+//
+// /**
+// * @param array $options
+// * options['params'] - callable ($model)
+// *
+// * @return \yii\web\Response
+// * @throws NotFoundHttpException
+// * @throws \yii\base\NotSupportedException
+// */
+// private function operate ($options) {
+// $model = $this->findModel($options['id']);
+// try {
+// $model->checkOperable();
+// $params = $options['params'] ? $options['params']($model) : ['id' => $model->id];
+// Server::perform($options['action'], $params);
+// \Yii::$app->getSession()->addFlash('success', [
+// 'title' => $model->name,
+// 'text' => \Yii::t('app', $options['successMessage']),
+// ]);
+// } catch (NotSupportedException $e) {
+// \Yii::$app->getSession()->addFlash('error', [
+// 'title' => $model->name,
+// 'text' => \Yii::t('app', $e->errorInfo),
+// ]);
+// } catch (HiResException $e) {
+// \Yii::$app->getSession()->addFlash('error', \Yii::t('app', $e->errorInfo));
+// }
+// return $this->actionView($options['id']);
+// }
+
+ protected function getOsimages () {
+ if (($models = Osimage::find()->all()) !== null) {
+ return $models;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+ protected function getOsimagesLiveCd () {
+ if (($models = Osimage::findAll(['livecd' => true])) !== null) {
+ return $models;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+ protected function getPanelTypes () {
+ return Ref::getList('type,panel');
+ }
+
+ protected function getStates () {
+ return Ref::getList('state,device');
+ }
+
+ /// TODO: XXX remove
+ public function actionList ($search = '', $id = null) {
+ $data = Server::find()->where(['server_like' => $search, 'ids' => $id])->getList();
+ $res = [];
+ foreach ($data as $key => $item) {
+ $res[] = ['id' => $key, 'text' => $item];
+ }
+ if (!empty($id)) $res = array_shift($res);
+
+ return $this->renderJson(['results' => $res]);
+ }
+
+ /**
+ * Generates array of osimages data, grouped by different fields to display on the website
+ *
+ * @param $images Array of osimages models to be proceed
+ *
+ * @return array
+ */
+ protected function getGroupedOsimages ($images) {
+ $isp = 1; /// TODO: temporary enabled for all tariff. Redo with check of tariff resources
+
+ $softpacks = [];
+ $oses = [];
+ $vendors = [];
+ foreach ($images as $image) {
+ /** @var Osimage $image */
+ $os = $image->os;
+ $name = $image->getFullOsName();
+ $panel = $image->getPanelName();
+ $system = $image->getFullOsName('');
+ $softpack_name = $image->getSoftPackName();
+ $softpack = $image->getSoftPack();
+
+ if (!array_key_exists($system, $oses)) {
+ $vendors[$os]['name'] = $os;
+ $vendors[$os]['oses'][$system] = $name;
+ $oses[$system] = ['vendor' => $os, 'name' => $name];
+ }
+
+ if ($panel != 'isp' || ($panel == 'isp' && $isp)) {
+ $data = [
+ 'name' => $softpack_name,
+ 'description' => preg_replace('/^ISPmanager - /', '', $softpack['description']),
+ 'osimage' => $image->osimage
+ ];
+
+ if ($softpack['soft']) {
+ $html_desc = [];
+ foreach ($softpack['soft'] as $soft => $soft_info) {
+ $soft_info['description'] = preg_replace('/,([^\s])/', ', $1', $soft_info['description']);
+
+ $html_desc[] = "{$soft_info['name']} {$soft_info['version']}: {$soft_info['description']}";
+ $data['soft'][$soft] = [
+ 'name' => $soft_info['name'],
+ 'version' => $soft_info['version'],
+ 'description' => $soft_info['description']
+ ];
+ }
+ $data['html_desc'] = implode("
", $html_desc);
+ }
+ $oses[$system]['panel'][$panel]['softpack'][$softpack_name] = $data;
+ $softpacks[$panel][$softpack_name] = $data;
+ } else {
+ $oses[$system]['panel'][$panel] = false;
+ }
+ }
+
+
+ foreach ($oses as $system => $os) {
+ $delete = true;
+ foreach ($os['panel'] as $panel => $info) {
+ if ($info !== false) $delete = false;
+ }
+ if ($delete) unset($vendors[$os['vendor']]['oses'][$system]);
+ }
+ return compact('vendors', 'oses', 'softpacks');
+ }
}
diff --git a/grid/ServerColumn.php b/grid/ServerColumn.php
new file mode 100644
index 00000000..133f9405
--- /dev/null
+++ b/grid/ServerColumn.php
@@ -0,0 +1,33 @@
+filterInputOptions['id']) {
+ $this->filterInputOptions['id'] = $this->attribute;
+ }
+ if (!$this->filter) {
+ $this->filter = Combo2::widget([
+ 'type' => 'server',
+ 'attribute' => $this->attribute,
+ 'model' => $this->grid->filterModel,
+ 'formElementSelector' => 'td',
+ ]);
+ };
+ }
+
+ public function getDataCellValue ($model, $key, $index) {
+ return Html::a($model->{$this->nameAttribute}, ['/server/server/view', 'id' => $model->{$this->attribute}]);
+ }
+}
diff --git a/models/Osimage.php b/models/Osimage.php
new file mode 100644
index 00000000..b7c871c0
--- /dev/null
+++ b/models/Osimage.php
@@ -0,0 +1,62 @@
+os, $this->version, $this->bitwise]);
+ }
+
+ public function getSoftPackName () { return $this->hasSoftPack() ? $this->softpack['name'] : 'clear'; }
+
+ public function hasSoftPack () { return !empty($this->softpack); }
+
+ public function getPanelName () { return $this->panel ?: 'no'; }
+
+ public function getSoftPack () { return $this->hasSoftPack() ? $this->softpack : []; }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels () {
+ return [
+ 'osimagae' => Yii::t('app', 'System name of image'),
+ 'os' => Yii::t('app', 'OS'),
+ 'version' => Yii::t('app', 'Version'),
+ 'bitwise' => Yii::t('app', 'Bitwise'),
+ 'panel' => Yii::t('app', 'Panel'),
+ 'softpack' => Yii::t('app', 'Soft package'),
+ ];
+ }
+}
diff --git a/models/OsimageSearch.php b/models/OsimageSearch.php
new file mode 100644
index 00000000..2f837178
--- /dev/null
+++ b/models/OsimageSearch.php
@@ -0,0 +1,55 @@
+ $query,
+ ]);
+
+ if (!($this->load($params) && $this->validate())) {
+ return $dataProvider;
+ }
+
+ $query->andFilterWhere([
+ 'osimage' => $this->osimage,
+ 'livecd' => $this->livecd
+ ]);
+
+ return $dataProvider;
+ }
+}
diff --git a/views/server/_vnc.php b/views/server/_vnc.php
new file mode 100644
index 00000000..1afec17f
--- /dev/null
+++ b/views/server/_vnc.php
@@ -0,0 +1,46 @@
+vnc['enabled']) {
+ echo Html::tag('span',
+ Html::tag('i', '', ['class' => 'glyphicon glyphicon-ok']) . ' ' . Yii::t('app', 'Enabled'),
+ ['class' => 'label label-success']);
+
+ $fields = [
+ 'IP' => $model->vnc['vnc_ip'],
+ 'Port' => $model->vnc['vnc_port'],
+ 'Password' => $model->vnc['vnc_password']
+ ];
+ ?>
+
Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?
+Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?
+Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?
+Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?
+