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'] + ]; + ?> +
+ $value) { ?> +
+
+ +
+ formatter->asRelativeTime($model->vnc['endTime'])); +} else { + echo Html::beginForm(['enable-vnc', 'id' => $model->id], "POST", ['data' => ['pjax' => 1], 'class' => 'inline']); + echo Html::submitButton( + Yii::t('app', 'Enable'), + [ + 'class' => 'btn btn-success', + 'data-loading-text' => Yii::t('app', 'Enabling...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit(); $(this).button('loading')"), + 'disabled' => !$model->isOperable() || !$model->isVNCSupported(), + ] + ); + echo ' '; + if (!$model->isVNCSupported()) { + echo Yii::t('app', 'VNC is supported only on XEN'); + } + echo Html::endForm(); +} diff --git a/views/server/index.php b/views/server/index.php index f7e1d7c6..7bd99c84 100644 --- a/views/server/index.php +++ b/views/server/index.php @@ -1,24 +1,128 @@ title = Yii::t('app', 'Servers'); -$this->params['breadcrumbs'][] = $this->title; -$this->params['subtitle'] = Yii::$app->request->queryParams ? 'filtered list' : 'full list'; +$this->title = Yii::t('app', 'Servers'); +$this->params['breadcrumbs'][] = $this->title; +Pjax::begin(array_merge(Yii::$app->params['pjax'], ['enablePushState' => true])); ?> +
+
+ $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + [ + 'class' => CheckboxColumn::className(), + ], + [ + 'class' => ResellerColumn::className(), + ], + [ + 'class' => ClientColumn::className(), + 'clientType' => 'ALL', + ], + [ + 'class' => ServerColumn::className(), + 'attribute' => 'id', + 'nameAttribute' => 'name' + ], + [ + 'attribute' => 'panel', + 'format' => 'text', + 'contentOptions' => ['class' => 'text-uppercase'], + 'value' => function ($model) { + return $model->panel ?: ''; + } + ], + 'tariff', + 'tariff_note', + [ - $dataProvider, - 'filterModel' => $searchModel, - 'columns' => [ - 'checkbox', - 'seller_id','client_id', - 'server' - ], -]) ?> + 'attribute' => 'discounts', + 'format' => 'raw', + 'value' => function ($model) { + return DiscountFormatter::widget([ + 'current' => $model->discounts['fee']['current'], + 'next' => $model->discounts['fee']['next'], + ]); + } + ], + [ + 'attribute' => 'state', + 'format' => 'raw', + 'value' => function ($model) { + return RequestState::widget([ + 'model' => $model, + 'module' => 'server' + ]); + }, + 'filter' => Html::activeDropDownList($searchModel, 'state', $states, [ + 'class' => 'form-control', + 'prompt' => Yii::t('app', '--'), + ]), + ], + [ + 'attribute' => 'sale_time', + 'format' => ['date'], + ], + [ + 'attribute' => 'os', + 'format' => 'raw', + 'value' => function ($model) use ($osimages) { + return frontend\modules\server\widgets\OSFormatter::widget([ + 'osimages' => $osimages, + 'imageName' => $model->osimage + ]); + } + ], + [ + 'attribute' => 'expires', + 'format' => 'raw', + 'value' => function ($model) { + return StateFormatter::widget([ + 'model' => $model + ]); + } + ], + [ + 'class' => 'yii\grid\ActionColumn', + 'template' => '{view}', + 'buttons' => [ + 'view' => function ($url, $model, $key) { + return GridActionButton::widget([ + 'url' => $url, + 'icon' => '', + 'label' => Yii::t('app', 'Details'), + ]); + }, + ], + ], + ], + ]); + ?> +
+
+title = Html::encode($model->domain); -$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Domains'), 'url' => ['index']]; +$this->title = Html::encode($model->name); +$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Servers'), 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; +Pjax::begin(Yii::$app->params['pjax']); ?> +
+
+
+
+
+ $model, + 'attributes' => [ + [ + 'attribute' => 'state', + 'format' => 'raw', + 'value' => RequestState::widget([ + 'model' => $model, + 'module' => 'server', + 'clientOptions' => [ + 'afterChange' => new \yii\web\JsExpression("function () { $.pjax.reload('#content-pjax', {'timeout': 0});}") + ] + ]) + ], + 'tariff', + 'tariff_note', + [ + 'attribute' => 'ips', + 'format' => 'raw', + 'value' => \frontend\components\widgets\ArraySpoiler::widget([ + 'data' => $model->ips + ]) + ], + [ + 'attribute' => 'os', + 'format' => 'raw', + 'value' => OSFormatter::widget([ + 'osimages' => $osimages, + 'imageName' => $model->osimage + ]) + ], + [ + 'attribute' => 'panel', + 'format' => 'raw', + // 'value' => switch ($model->panel) { + // case 'isp': + // return Html::a('isp', "http://{$model->ip}:1500/", ['target' => '_blank']); + // break; + // default: + // return $model->panel; + // break; + // } + // } + ], + [ + 'attribute' => 'sale_time', + 'format' => 'date' + ], + [ + 'attribute' => 'discounts', + 'format' => 'raw', + 'value' => DiscountFormatter::widget([ + 'current' => $model->discounts['fee']['current'], + 'next' => $model->discounts['fee']['next'], + ]) + ], + [ + 'attribute' => 'expires', + 'format' => 'raw', + 'value' => StateFormatter::widget([ + 'model' => $model + ]) + ] + ], + ]) ?> +
+
+
+
-params['pjax']) ?> -
- -
- $model, - 'columns' => [ - 'seller_id','client_id', - ['attribute' => 'server'], - ], - ]) ?> -
+
+
+
+
+ render('_vnc', ['model' => $model]) ?> +
+
+
+ +
+
+
+
+ ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Reboot'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Confirm server reboot')), + 'headerOptions' => ['class' => 'label-warning'], + 'footer' => Html::button(Yii::t('app', 'Reboot'), [ + 'class' => 'btn btn-warning', + 'data-loading-text' => Yii::t('app', 'Rebooting...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit();") + ]) + ]); + ?> +
+

+ +

Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?

+
+ + + ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Reset'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Confirm server reset')), + 'headerOptions' => ['class' => 'label-warning'], + 'footer' => Html::button(Yii::t('app', 'Reset'), [ + 'class' => 'btn btn-warning', + 'data-loading-text' => Yii::t('app', 'Resetting...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit(); $(this).button('loading')") + ]) + ]); + ?> +
+

+ +

Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?

+
+ + + ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Shutdown'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Confirm server shutdown')), + 'headerOptions' => ['class' => 'label-warning'], + 'footer' => Html::button(Yii::t('app', 'Shutdown'), [ + 'class' => 'btn btn-warning', + 'data-loading-text' => Yii::t('app', 'Shutting down...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit(); $(this).button('loading')") + ]) + ]); + ?> +
+

+ +

Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?

+
+ + ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Power off'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Confirm server power off')), + 'headerOptions' => ['class' => 'label-warning'], + 'footer' => Html::button(Yii::t('app', 'Power OFF'), [ + 'class' => 'btn btn-warning', + 'data-loading-text' => Yii::t('app', 'Turning power OFF...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit(); $(this).button('loading')") + ]) + ]); + ?> +
+

+ +

Кароч, тут может все нах похерится. Сечёшь? Точняк уверен?

+
+ + + ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Power on'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Confirm server power ON')), + 'footer' => Html::button(Yii::t('app', 'Power ON'), [ + 'class' => 'btn btn-info', + 'data-loading-text' => Yii::t('app', 'Turning power ON...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit(); $(this).button('loading')") + ]) + ]); + ?> + Включить сервер? + +
+
+
+ +
+
+
+
+ isLiveCDSupported()) { + echo Html::beginForm(['boot-live'], "POST", ['data' => ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + + $os_items = []; + foreach ($osimageslivecd as $item) { + $js = "$(this).closest('form').find('.livecd-osimage').val({$item['osimage']}).end().submit(); $(this).closest('button').button('loading');"; + $os_items[] = [ + 'label' => $item['os'] . ' ' . $item['bitwise'], + 'url' => '#', + 'onclick' => new \yii\web\JsExpression($js) + ]; + + } + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Live CD'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Confirm boot from Live CD')), + 'footer' => \yii\bootstrap\ButtonDropdown::widget([ + 'label' => 'Boot LiveCD', + 'dropdown' => [ + 'items' => $os_items + ], + 'options' => [ + 'class' => 'btn btn-info', + 'data-loading-text' => Yii::t('app', 'Resetting password...'), + ] + ]) + ]); + echo Html::hiddenInput('osimage', null, ['class' => 'livecd-osimage']); + ?> + Это приведет к отключению сервера и загрузке образа Live CD. + ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Reset password'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Confirm server password resetting')), + 'headerOptions' => ['class' => 'label-warning'], + 'footer' => Html::button(Yii::t('app', 'Reset root password'), [ + 'class' => 'btn btn-warning', + 'data-loading-text' => Yii::t('app', 'Resetting password...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit(); $(this).button('loading')") + ]) + ]); + ?> + + + + + ['pjax' => 1], 'class' => 'inline']); + echo Html::hiddenInput('id', $model->id); + Modal::begin([ + 'toggleButton' => [ + 'label' => Yii::t('app', 'Reinstall OS'), + 'class' => 'btn btn-default', + 'disabled' => !$model->isOperable(), + ], + 'header' => Html::tag('h4', Yii::t('app', 'Please, select the operating system you want to install')), + 'headerOptions' => ['class' => 'label-warning'], + 'footer' => Html::button(Yii::t('app', 'Reinstall'), [ + 'class' => 'btn btn-warning', + 'data-loading-text' => Yii::t('app', 'Reinstalling started...'), + 'onClick' => new \yii\web\JsExpression("$(this).closest('form').submit(); $(this).button('loading');") + ]) + ]); + ?> +
+

+
+ "reinstall-osimage"]) ?> + "reinstall-panel"]) ?> +
+
+
+
+
+ +
+

+ +
+ $os) { + echo Html::tag('div', Html::radio('os', false, [ + 'label' => $os, + 'value' => $system, + 'class' => 'radio' + ]), ['class' => 'radio']); + } ?> +
+
+ +
+
+
+
+
+
+
+ $panel_name) { ?> +
+

+ +
+ +
+ +
+ +
+
+ +
+
+
+
+ +
+
+
- + +registerJs("var osparams = " . Json::encode($grouped_osimages['oses']) . "; + $('.os-selector').osSelector({ + osparams: osparams + }); + ", \yii\web\View::POS_READY, 'os-selector-init'); ?> +current = floatval($this->current); + $this->next = floatval($this->next); + } + + public function run () { + $this->getView()->registerJs("$('.discount-popover').popover();", \yii\web\View::POS_READY, 'discount-popover'); + + return Html::tag($this->tagName, + \Yii::$app->formatter->asPercent($this->current/100), + [ + 'title' => \Yii::t('app', 'Next discount'), + 'class' => 'btn btn-default btn-xs discount-popover', + 'data-trigger' => 'focus', + 'data-content' => \Yii::$app->formatter->asPercent($this->next/100), + ]); + } +} diff --git a/widgets/OSFormatter.php b/widgets/OSFormatter.php new file mode 100644 index 00000000..23b4821c --- /dev/null +++ b/widgets/OSFormatter.php @@ -0,0 +1,92 @@ +osimages as $osimage) { + if ($osimage->osimage == $this->imageName) { + $this->osimage = $osimage; + break; + } + } + } + + /** + * @return string html code of definition list + */ + public function generateOSInfo () { + $html = Html::beginTag('dl', ['class' => 'dl-horizontal']); + + foreach ($this->osimage->softpack['soft'] as $item) { + $html .= Html::tag('dt', $item['name'] . ' ' . $item['version']); + $html .= Html::tag('dd', $item['description']); + } + + $html .= Html::endTag('dl'); + + return $html; + } + + /** + * Renders info-circle with modal popup + */ + public function generateInfoCircle () { + Modal::begin([ + 'toggleButton' => [ + 'class' => 'fa fa-info-circle text-info os-info-popover', + 'label' => '' + ], + 'header' => Html::tag('h4', $this->osimage->getFullOsName()), + 'size' => Modal::SIZE_LARGE + ]); + echo Html::tag('div', $this->generateOSInfo(), [ + 'class' => 'row' + ]); + Modal::end(); + } + + /** + * Renders the widget + */ + public function run () { + if (!($this->osimage instanceof Osimage)) return $this->imageName; + + echo $this->osimage->getFullOsName(); + echo " "; + if ($this->osimage->hasSoftPack() && $this->infoCircle) echo $this->generateInfoCircle(); + } +} diff --git a/widgets/StateFormatter.php b/widgets/StateFormatter.php new file mode 100644 index 00000000..1882d0cb --- /dev/null +++ b/widgets/StateFormatter.php @@ -0,0 +1,47 @@ +model instanceof Server)) { + throw new InvalidParamException("Model should be an instance of Server model"); + } + } + + /** + * @return string + */ + public function run () { + if ($this->model->state != 'blocked') { + $value = \yii::$app->formatter->asDate($this->model->expires); + } else { + $value = \yii::t('app', 'Blocked') . ' ' . Re::l($this->model->block_reason_label); + } + + $class = ['label']; + + if (strtotime("+7 days", time()) < strtotime($this->model->expires)) { + $class[] = 'label-info'; + } elseif (strtotime("+3 days", time()) < strtotime($this->model->expires)) { + $class[] = 'label-warning'; + } else { + $class[] = 'label-danger'; + } + $html = Html::tag('span', $value, ['class' => implode(' ', $class)]); + + return $html; + } +} diff --git a/widgets/VNCFormatter.php b/widgets/VNCFormatter.php new file mode 100644 index 00000000..672b1c26 --- /dev/null +++ b/widgets/VNCFormatter.php @@ -0,0 +1,26 @@ +getView()->registerJs("$('.discount-popover').popover();", \yii\web\View::POS_READY, 'discount-popover'); + + return Html::tag($this->tagName, + \Yii::$app->formatter->asPercent($this->current/100), + [ + 'title' => \Yii::t('app', 'Next discount'), + 'class' => 'btn btn-default btn-xs discount-popover', + 'data-trigger' => 'focus', + 'data-content' => \Yii::$app->formatter->asPercent($this->next/100), + ]); + } +}