/** * Render ActiveField input based on provided settings * * @param ActiveForm $form * @param Model|ActiveRecord $model * @param string $attribute * @param array $settings * @return ActiveField|NULL|string */ private function renderActiveInput($form, $model, $attribute, $settings) { if (is_string($settings) && $settings) { // no settings have been provided for the attribute so $settings will be the attribute name $attribute = $settings; $settings = []; } $settingsIn = $settings; if ($model instanceof AttributeSupportInterface) { //$activeConfig = $model->getAttributeConfig($attribute, 'active'); $activeConfig = $model->getActiveFieldSettings($attribute); if ($activeConfig) { $settings = array_merge($activeConfig, $settings); } } $spec = false; if ($model instanceof YiiActiveRecord) { if ($this->spec === null) { $this->spec = $this->model->getTableSchema()->columns; } if ($this->spec && isset($this->spec[$attribute])) { $spec = isset($this->spec[$attribute]) ? $this->spec[$attribute] : false; } } $type = ArrayHelper::getValue($settings, 'type', null); $typeAutomatic = false; $label = null; $items = ArrayHelper::getValue($settings, 'items', []); if ($items instanceof \Closure) { $items = $items($model, $attribute); } if (!$items) { if (is_array($spec->enumValues) && $spec->enumValues) { foreach ($spec->enumValues as $k => $v) { if (is_string($v) && $v == '') { } else { $items[$v] = $v; } } } } $allowClear = false; if ($type === null && $items) { // we have been given items so assume a drop down list $type = InputField::INPUT_DROPDOWN_LIST; } if ($type === null) { // try to guess the appropriate type switch ($attribute) { case 'created_at': case 'createdAt': $type = InputField::INPUT_STATIC; $label = $model->getAttributeConfig($attribute, 'label'); $label = !$label ? ArrayHelper::getValue($settings, 'label', false) : $label; $label = !$label ? 'Created' : $label; $createAttribute = $attribute == 'created_at' ? 'created_by' : 'createdBy'; if ($model->hasAttribute($createAttribute)) { if ($model->{$createAttribute} == Yii::$app->user->identity->id) { $settings['options']['value'] = $model->{$attribute} . ' by ' . Yii::$app->user->identity->display_name; } else { $createData = AdminUser::find()->select('display_name')->where(['id' => $model->{$createAttribute}])->limit(1)->asArray()->column(); $settings['options']['value'] = $model->{$attribute} . ' by ' . $createData[0]; } } break; case 'modified_at': case 'modifiedAt': $type = InputField::INPUT_STATIC; $label = $model->getAttributeConfig($attribute, 'label'); $label = !$label ? ArrayHelper::getValue($settings, 'label', false) : $label; $label = !$label ? 'Last Modified' : $label; $modifiedAttribute = $attribute == 'modified_at' ? 'modified_by' : 'modifiedBy'; if ($model->hasAttribute($modifiedAttribute)) { if ($model->{$modifiedAttribute} == Yii::$app->user->identity->id) { $settings['options']['value'] = $model->{$attribute} . ' by ' . Yii::$app->user->identity->display_name; } else { $modifiedData = AdminUser::find()->select('display_name')->where(['id' => $model->{$modifiedAttribute}])->limit(1)->asArray()->column(); $settings['options']['value'] = $model->{$attribute} . ' by ' . $modifiedData[0]; } } break; case 'created_by': case 'createdBy': case 'modified_by': case 'modifiedBy': case 'id': $type = InputField::INPUT_STATIC; break; default: $type = InputField::getDefaultInputFieldType($attribute, $spec, $settings); if ($type) { $typeAutomatic = true; } } if ($type === null) { return ''; } } $fieldConfig = ArrayHelper::getValue($settings, 'fieldConfig', []); $options = ArrayHelper::getValue($settings, 'options', []); $hint = ArrayHelper::getValue($settings, 'hint', null); $icon = ArrayHelper::getValue($settings, 'icon', $typeAutomatic && InputField::getIsIconSupportedFieldType($type) ? true : null); $tooltip = ArrayHelper::getValue($settings, 'tooltip', null); $focus = ArrayHelper::getValue($settings, 'focus', null); $addon = ArrayHelper::getValue($settings, 'addon', null); $clear = ArrayHelper::getValue($settings, 'clear', null); $label = $label === null ? ArrayHelper::getValue($settingsIn, 'label', $label) : $label; $label = $label === null ? $this->model->getAttributeLabel($attribute) : $label; $hideuseInlineHelp = $useLabelColumn = ArrayHelper::getValue($settings, 'useInlineHelp', true); // is the current field read only $readOnly = false; if ($type == InputField::INPUT_READONLY) { $type = InputField::INPUT_TEXT; $readOnly = true; } elseif (!$this->ignoreReadOnly && $model instanceof ActiveRecordReadOnlyInterface && $model->getReadOnly()) { $readOnly = true; } elseif (method_exists($form, 'isEditLocked') && $form->isEditLocked()) { $readOnly = true; } elseif (!$this->ignoreIsEditLocked && method_exists($model, 'isEditLocked') && $model->isEditLocked()) { $readOnly = true; } elseif (ArrayHelper::getValue($settings, 'readonly', false)) { $readOnly = true; } /* * apply any conversions if required or extra settings to apply */ switch ($type) { case InputField::INPUT_CHECKBOX: if (!ArrayHelper::keyExists('class', $options)) { // should we apply a default checkbox style or plugin if ($this->defaultCheckboxPlugin !== null && $this->defaultCheckboxPlugin) { Html::addCssClass($options, $this->defaultCheckboxPlugin); } } break; case InputField::INPUT_CHECKBOX_BASIC: // revert back to basic checkbox $type = InputField::INPUT_CHECKBOX; break; case InputField::INPUT_CHECKBOX_ICHECK: // revert to basic checkbox with icheck class applied $type = InputField::INPUT_CHECKBOX; Html::addCssClass($options, 'icheck'); break; case InputField::INPUT_CHECKBOX_SWITCH: // revert to basic checkbox with make-switch class applied $type = InputField::INPUT_CHECKBOX; Html::addCssClass($options, 'make-switch'); break; case InputField::INPUT_DATE: $options['data-maxlength'] = false; $options['data-inputmask'] = ['alias' => 'yyyy-mm-dd']; $options['maxlength'] = 10; $addon = ['append' => [['class' => 'glyphicon glyphicon-th show-datepicker', 'title' => 'Click to select date']], 'groupOptions' => ['class' => 'date input-small']]; //$options['widgetOptions']['pluginOptions']['autoclose'] = false; // optionally override plugin or widget options $clear = $clear !== false ? true : $clear; $allowClear['input'] = 'input'; break; case InputField::INPUT_DATETIME: $options['data-maxlength'] = false; $options['data-inputmask'] = ['alias' => 'yyyy-mm-dd hh:mm:ss']; //$options['data-inputmask'] = ['alias' => 'yyyy-mm-dd hh:mm']; $options['maxlength'] = 19; $addon = ['append' => [['class' => 'glyphicon glyphicon-th show-date-time-picker', 'title' => 'Click to select date']], 'groupOptions' => ['class' => 'date input-medium']]; //$options['widgetOptions']['pluginOptions']['autoclose'] = false; // optionally override plugin or widget options $clear = $clear !== false ? true : $clear; $allowClear['input'] = 'input'; break; case InputField::INPUT_YEAR: $options['data-maxlength'] = false; $options['data-inputmask'] = ['mask' => '9999']; break; case InputField::INPUT_TIME: $options['data-maxlength'] = false; $options['data-inputmask'] = ['alias' => 'hh:mm:ss']; //$options['data-inputmask'] = ['alias' => 'hh:mm']; $options['maxlength'] = 8; $addon = ['append' => [['class' => 'glyphicon glyphicon-th clickable show-timepicker', 'title' => 'Click to select time']], 'groupOptions' => ['class' => 'input-small']]; $clear = $clear !== false ? true : $clear; $allowClear['input'] = 'time'; break; case InputField::INPUT_INTEGER: $options['data-maxlength'] = false; $options['data-inputmask'] = isset($options['data-inputmask']) ? $options['data-inputmask'] : []; $options['maxlength'] = isset($options['maxlength']) ? $options['maxlength'] : $spec->size + ($spec->unsigned ? 0 : 1); $defaults = ['alias' => 'numeric', 'allowMinus' => !$spec->unsigned, 'digits' => 0, 'rightAlign' => true]; $options['data-inputmask'] = array_merge($defaults, $options['data-inputmask']); $clear = $clear !== false ? true : $clear; $allowClear['input'] = 'integer'; //$allowClear['value'] = '0'; break; case InputField::INPUT_DECIMAL: $options['data-maxlength'] = false; $options['data-inputmask'] = isset($options['data-inputmask']) ? $options['data-inputmask'] : []; $options['maxlength'] = isset($options['maxlength']) ? $options['maxlength'] : $spec->size + 1 + ($spec->unsigned ? 0 : 1); $defaults = ['alias' => 'decimal', 'allowMinus' => !$spec->unsigned, 'integerDigits' => $spec->size - $spec->scale, 'digits' => $spec->scale, 'rightAlign' => true]; $options['data-inputmask'] = array_merge($defaults, $options['data-inputmask']); $clear = $clear !== false ? true : $clear; $allowClear['input'] = 'decimal'; if ($spec->scale != 2) { $allowClear['value'] = number_format(0, $spec->scale); } break; case InputField::INPUT_COLOR: $options['data-maxlength'] = false; $allowClear['input'] = 'colorpicker'; break; case InputField::INPUT_MINI_COLORS: $options['data-maxlength'] = false; $allowClear['input'] = 'minicolors'; break; case InputField::INPUT_TEXT: case InputField::INPUT_TEXTAREA: $allowClear['input'] = 'input'; break; case InputField::INPUT_DROPDOWN_LIST: case InputField::INPUT_LIST_BOX: // will select first item in drop down list $allowClear['input'] = 'select'; break; case InputField::INPUT_SELECT2: case InputField::INPUT_SELECT2_MULTI: $allowClear['input'] = 'select2'; break; case InputField::INPUT_SELECT_PICKER: case InputField::INPUT_SELECT_PICKER_MULTI: $allowClear['input'] = 'selectpicker'; break; case InputField::INPUT_MULTISELECT: $allowClear['input'] = 'multiselect'; break; default: } /** * @var ActiveField $field */ $field = $form->field($model, $attribute, $fieldConfig); switch ($type) { case InputField::INPUT_HIDDEN: case InputField::INPUT_STATIC: return static::getInput($field, $type, [$options], $label, $hint, $icon, $tooltip, null); break; case InputField::INPUT_TEXT: case InputField::INPUT_PASSWORD: case InputField::INPUT_PASSWORD_STRENGTH: case InputField::INPUT_TEXTAREA: case InputField::INPUT_INTEGER: case InputField::INPUT_DECIMAL: case InputField::INPUT_YEAR: case InputField::INPUT_TIME: case InputField::INPUT_DATE: case InputField::INPUT_DATETIME: case InputField::INPUT_COLOR: case InputField::INPUT_MINI_COLORS: Html::addCssClass($options, 'form-control'); if (ArrayHelper::getValue($settings, 'placeholder', null) !== null) { $options['placeholder'] = ArrayHelper::getValue($settings, 'placeholder', null); } if ($readOnly) { $options['disabled'] = 'disabled'; $clear = false; } $clear = $clear === true ? true : false; // null defaults to false if still null at this stage if ($clear === false) { $allowClear = false; } $maxlength = ArrayHelper::getValue($options, 'maxlength', $spec ? $spec->size : 0); $inputSize = ArrayHelper::getValue($settings, 'size', InputField::INPUT_SIZE_AUTO); if ($maxlength) { $inputSize = $this->getDefaultInputSize($inputSize, $maxlength, $icon, $tooltip); if (!$readOnly) { $options['maxlength'] = $maxlength; if (isset($options['data-maxlength']) && !$options['data-maxlength']) { // do not use the plugin unset($options['data-maxlength']); } else { BootstrapMaxlengthAsset::register($this->getView()); if (!isset($options['data-maxlength']['threshold'])) { if ($maxlength > 99) { $options['data-maxlength']['threshold'] = '50'; } elseif ($maxlength > 50) { $options['data-maxlength']['threshold'] = '20'; } elseif ($maxlength > 10) { $options['data-maxlength']['threshold'] = '10'; } else { $options['data-maxlength']['threshold'] = $maxlength - 1; } } if (!isset($options['data-maxlength']['placement'])) { $options['data-maxlength']['placement'] = 'top'; } $options['data-maxlength'] = Json::encode($options['data-maxlength']); Html::addCssClass($options, 'bs-max-me'); } if ($type == InputField::INPUT_PASSWORD_STRENGTH) { BootstrapPasswordStrengthAsset::register($this->getView()); Html::addCssClass($options, 'strength-me'); } $options = $this->setFocus($options, $field, $attribute, $focus); } } if (isset($options['data-inputmask'])) { Html::addCssClass($options, 'mask-me'); if (isset($options['data-inputmask'])) { $options['data-inputmask'] = Json::encode($options['data-inputmask']); } InputMaskAsset::register($this->getView()); } $options = $this->applyInputSize($inputSize, $options); // by default make inputs select their own content when they get focus Html::addCssClass($options, 'select-me'); $options = $this->convertOptionsForWidgets($type, $options); if ($allowClear) { $allowClear['size'] = $inputSize; } return static::getInput($field, $type, [$options], $label, $hint, $icon, $tooltip, $addon, $allowClear); break; case InputField::INPUT_FILE: return static::getInput($field, $type, [$options], $label, $hint, $icon, $tooltip, $addon); break; case InputField::INPUT_CHECKBOX: $enclosedByLabel = ArrayHelper::getValue($settings, 'enclosedByLabel', false); $useLabelColumn = ArrayHelper::getValue($settings, 'useLabelColumn', true); if ($label !== null && $label) { $options['label'] = $useLabelColumn ? null : $label; } $useInlineHelp = ArrayHelper::getValue($settings, 'useInlineHelp', true); if ($useInlineHelp) { $field->hintOptions = ['tag' => 'span', 'class' => 'help-inline']; $field->template = str_replace("\n{error}", '', $field->template); } if ($readOnly) { $options['disabled'] = 'disabled'; } if (strpos(ArrayHelper::getValue($options, 'class', ''), 'make-switch') !== false) { //$options['data-on-text'] = ArrayHelper::getValue($options, 'data-on-text', 'ON'); // value can be an icon e.g. <i class='fa fa-check'></i> //$options['data-off-text'] = ArrayHelper::getValue($options, 'data-off-text', 'OFF'); // value can be an icon e.g. <i class='fa fa-times'></i> //$options['data-on-color'] = ArrayHelper::getValue($options, 'data-on-color', 'primary'); //$options['data-off-color'] = ArrayHelper::getValue($options, 'data-off-color', 'default'); $options['data-size'] = ArrayHelper::getValue($options, 'data-size', 'small'); BootstrapSwitchAsset::register($this->getView()); } elseif (strpos(ArrayHelper::getValue($options, 'class', ''), 'icheck') !== false) { //Html::addCssClass($options, 'icheck'); $options['data-checkbox'] = ArrayHelper::getValue($options, 'data-checkbox', 'icheckbox_square-blue'); ICheckAsset::register($this->getView()); } return $this->getInput($field->{$type}($options, $enclosedByLabel), $type, null, $useLabelColumn ? $label : null, $hint); break; case InputField::INPUT_RAW: $value = ArrayHelper::getValue($settings, 'value', ''); if ($value instanceof \Closure) { $value = $value(); } return $value; break; case InputField::INPUT_SELECT_PICKER_MULTI: case InputField::INPUT_SELECT2_MULTI: case InputField::INPUT_MULTISELECT: $isMultiple = true; // no break // no break case InputField::INPUT_DROPDOWN_LIST: case InputField::INPUT_LIST_BOX: case InputField::INPUT_SELECT2: case InputField::INPUT_SELECT_PICKER: case InputField::INPUT_SELECT_SPLITTER: $isMultiple = isset($isMultiple) && $isMultiple || isset($options['multiple']) ? true : false; if ($isMultiple) { $options['multiple'] = 'multiple'; $this->model->setAttribute($attribute, explode('|', $this->model->getAttribute($attribute))); } if ($isMultiple && !InputField::getIsWidgetFromFieldType($type) || $type == InputField::INPUT_LIST_BOX) { $options['size'] = ArrayHelper::getValue($options, 'size', 4); } Html::addCssClass($options, 'form-control'); if (ArrayHelper::getValue($settings, 'placeholder', null) !== null) { $options['prompt'] = ArrayHelper::getValue($settings, 'placeholder', null); } if ($readOnly) { $options['disabled'] = 'disabled'; $clear = false; } else { $options = $this->setFocus($options, $field, $attribute, $focus); } $clear = $clear === true ? true : false; // null defaults to false if still null at this stage if ($clear === false) { $allowClear = false; } if ($typeAutomatic && !isset($options['prompt'])) { if ($model->hasAttribute($attribute) && $model->getAttribute($attribute) != '' && $model->getAttribute($attribute) !== null) { // no prompt required by default } else { $options['prompt'] = 'Select...'; $options['promptValue'] = ''; } } switch ($type) { case InputField::INPUT_SELECT2: case InputField::INPUT_SELECT2_MULTI: case InputField::INPUT_SELECT_PICKER: case InputField::INPUT_SELECT_PICKER_MULTI: case InputField::INPUT_SELECT_SPLITTER: if (isset($options['prompt']) && $options['prompt'] != '') { // default value will be blank and convert to null by default if ($type == InputField::INPUT_SELECT2 || $type == InputField::INPUT_SELECT2_MULTI) { $promptValue = isset($options['promptValue']) ? $options['promptValue'] : '0'; if (!isset($options['groups'])) { $options['widgetOptions']['pluginOptions']['placeholder'] = $options['prompt']; $options['widgetOptions']['pluginOptions']['id'] = $promptValue; } else { $promptValue = $options['promptValue']; $items = array_merge([$promptValue => $options['prompt']], $items); } } elseif ($type == InputField::INPUT_SELECT_PICKER || $type == InputField::INPUT_SELECT_PICKER_MULTI) { $promptValue = isset($options['promptValue']) ? $options['promptValue'] : ''; if ($promptValue != '') { $items = array_merge([$promptValue => $options['prompt']], $items); } else { $options['title'] = $options['prompt']; if (!$isMultiple) { //wlchere - makes it work well front end but breaks back end when an array is submitted //$isMultiple = true; //$options['multiple'] = 'multiple'; //$options['widgetOptions']['pluginOptions']['maxOptions'] = 1; } } } elseif ($type == InputField::INPUT_SELECT_SPLITTER) { if (!$isMultiple) { $promptValue = isset($options['promptValue']) ? $options['promptValue'] : ''; $items = array_merge(['' => [$promptValue => $options['prompt']]], $items); $options['groups'] = isset($options['groups']) ? $options['groups'] : []; $options['groups'] = array_merge(['' => ['label' => $options['prompt']]], $options['groups']); } } } break; default: if (!$isMultiple && isset($options['prompt']) && $options['prompt'] != '') { // default value will be blank and convert to null by default $promptValue = isset($options['promptValue']) ? $options['promptValue'] : '0'; $items = array_merge([$promptValue => $options['prompt']], $items); } } unset($options['prompt']); unset($options['promptValue']); $inputSize = ArrayHelper::getValue($settings, 'size', InputField::INPUT_SIZE_AUTO); $inputSize = $this->getDefaultInputSize($inputSize, 0, $icon, $tooltip); $options = $this->applyInputSize($inputSize, $options); $options = $this->convertOptionsForWidgets($type, $options); if ($allowClear) { $allowClear['size'] = $inputSize; } return $this->getInput($field, $type, [$items, $options], $label, $hint, $icon, $tooltip, $addon, $allowClear); break; case InputField::INPUT_CHECKBOX_LIST: case InputField::INPUT_CHECKBOX_LIST_ICHECK: $this->model->setAttribute($attribute, explode('|', $this->model->getAttribute($attribute))); $allowClear['input'] = 'checkbox'; if (!isset($options['itemOptions'])) { $options['itemOptions'] = []; } if (!isset($options['itemOptions']['labelOptions'])) { $options['itemOptions']['labelOptions'] = []; } $inline = ArrayHelper::getValue($settings, 'inline', false); if ($inline) { Html::addCssClass($options['itemOptions']['labelOptions'], 'checkbox-inline'); } else { // vertically listed so disable form control as well $settings['noFormControl'] = true; } if (ArrayHelper::getValue($settings, 'noFormControl', false)) { // icon and tooltip will not be supported $icon = false; $tooltip = false; Html::addCssClass($options, 'checkbox-list'); $allowClear = false; } else { // wrap the whole checkbox list in a form-control box Html::addCssClass($options, 'form-control fc-checkbox-list checkbox-list'); } if ($type == InputField::INPUT_CHECKBOX_LIST_ICHECK) { $allowClear['input'] = 'icheckbox'; Html::addCssClass($options['itemOptions'], 'icheck'); $options['itemOptions']['data-checkbox'] = ArrayHelper::getValue($options['itemOptions'], 'data-checkbox', 'icheckbox_square-blue'); ICheckAsset::register($this->getView()); } if ($readOnly) { $options['disabled'] = 'disabled'; $options['itemOptions']['disabled'] = 'disabled'; $clear = false; } if ($allowClear) { $clear = $clear === true ? true : false; // null defaults to false if still null at this stage if ($clear === false) { $allowClear = false; } } $options = $this->applyInputSize(ArrayHelper::getValue($settings, 'size', InputField::INPUT_SIZE_NONE), $options); return $this->getInput($field, 'checkboxList', [$items, $options], $label, $hint, $icon, $tooltip, $addon, $allowClear); break; case InputField::INPUT_RADIO_LIST: case InputField::INPUT_RADIO_LIST_ICHECK: $allowClear['input'] = 'radio'; if (!isset($options['itemOptions'])) { $options['itemOptions'] = []; } if (!isset($options['itemOptions']['labelOptions'])) { $options['itemOptions']['labelOptions'] = []; } $inline = ArrayHelper::getValue($settings, 'inline', false); if ($inline) { Html::addCssClass($options['itemOptions']['labelOptions'], 'radio-inline'); } else { // vertically listed so disable form control as well $settings['noFormControl'] = true; } if (ArrayHelper::getValue($settings, 'noFormControl', false)) { // icon and tooltip will not be supported $icon = false; $tooltip = false; Html::addCssClass($options, 'radio-list'); $allowClear = false; } else { // wrap the whole checkbox list in a form-control box Html::addCssClass($options, 'form-control fc-radio-list radio-list'); } if ($type == InputField::INPUT_RADIO_LIST_ICHECK) { $allowClear['input'] = 'iradio'; if (!$inline) { Html::addCssClass($options['itemOptions']['labelOptions'], 'radio-vertical'); } Html::addCssClass($options['itemOptions'], 'icheck'); $options['itemOptions']['data-radio'] = ArrayHelper::getValue($options['itemOptions'], 'data-radio', 'iradio_square-blue'); ICheckAsset::register($this->getView()); } if ($readOnly) { $options['disabled'] = 'disabled'; $options['itemOptions']['disabled'] = 'disabled'; $clear = false; } if ($allowClear) { $clear = $clear === true ? true : false; // null defaults to false if still null at this stage if ($clear === false) { $allowClear = false; } } $options = $this->applyInputSize(ArrayHelper::getValue($settings, 'size', InputField::INPUT_SIZE_NONE), $options); return $this->getInput($field, 'radioList', [$items, $options], $label, $hint, $icon, $tooltip, $addon, $allowClear); break; case InputField::INPUT_HTML5: return 'WORK IN PROGRESS: ' . $attribute . ' : ' . $type . '<br/>'; break; case InputField::INPUT_EDITOR_CK: case InputField::INPUT_EDITOR_BS_WYSIHTML5: case InputField::INPUT_EDITOR_BS_SUMMERNOTE: //wlchere move to normal place above plus possibly switch this whole section into textarea above // and make use of InputField::getIsEditorFromFieldType($type) $allowClear['input'] = 'val'; Html::addCssClass($options, 'form-control'); if (ArrayHelper::getValue($settings, 'placeholder', null) !== null) { $options['placeholder'] = ArrayHelper::getValue($settings, 'placeholder', null); } if ($readOnly) { $options['disabled'] = 'disabled'; $clear = false; } $clear = $clear === true ? true : false; // null defaults to false if still null at this stage if ($clear === false) { $allowClear = false; } $inputSize = ArrayHelper::getValue($settings, 'size', InputField::INPUT_SIZE_AUTO); $options = $this->applyInputSize($inputSize, $options); $options = $this->convertOptionsForWidgets($type, $options); if ($allowClear) { $allowClear['size'] = $inputSize; } return static::getInput($field, $type, [$options], $label, $hint, $icon, $tooltip, $addon, $allowClear); break; case InputField::INPUT_SELECT2_TAGS: case InputField::INPUT_WIDGET: return 'WORK IN PROGRESS (other): ' . $attribute . ' : ' . $type . '<br/>'; break; case InputField::INPUT_RADIO: return 'Not currently supported: ' . $attribute . ' : ' . $type . '<br/>'; break; default: return 'WORK IN PROGRESS (other): ' . $attribute . ' : ' . $type . '<br/>'; } return null; }
/** * @param ActiveRecord $target * @param int $key * @param int $levelUp * @throws Exception * @return boolean */ private function moveNode($target, $key, $levelUp) { if ($this->owner->getIsNewRecord()) { throw new Exception('The node should not be new record.'); } if ($this->getIsDeletedRecord()) { throw new Exception('The node should not be deleted.'); } if ($target->getIsDeletedRecord()) { throw new Exception('The target node should not be deleted.'); } if ($this->owner->equals($target)) { throw new Exception('The target node should not be self.'); } if ($target->isDescendantOf($this->owner)) { throw new Exception('The target node should not be descendant.'); } if (!$levelUp && $target->isRoot()) { throw new Exception('The target node should not be root.'); } if (!$this->beforeMoveNode($this->_previousPath)) { return false; } $db = $this->owner->getDb(); if ($db->getTransaction() === null) { $transaction = $db->beginTransaction(); } try { $left = $this->owner->getAttribute($this->leftAttribute); $right = $this->owner->getAttribute($this->rightAttribute); $levelDelta = $target->getAttribute($this->levelAttribute) - $this->owner->getAttribute($this->levelAttribute) + $levelUp; if ($this->hasManyRoots && $this->owner->getAttribute($this->rootAttribute) !== $target->getAttribute($this->rootAttribute)) { foreach ([$this->leftAttribute, $this->rightAttribute] as $attribute) { $this->owner->updateAll([$attribute => new Expression($db->quoteColumnName($attribute) . sprintf('%+d', $right - $left + 1))], $db->quoteColumnName($attribute) . '>=' . $key . ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute, [':' . $this->rootAttribute => $target->getAttribute($this->rootAttribute)]); } $delta = $key - $left; $this->owner->updateAll([$this->leftAttribute => new Expression($db->quoteColumnName($this->leftAttribute) . sprintf('%+d', $delta)), $this->rightAttribute => new Expression($db->quoteColumnName($this->rightAttribute) . sprintf('%+d', $delta)), $this->levelAttribute => new Expression($db->quoteColumnName($this->levelAttribute) . sprintf('%+d', $levelDelta)), $this->rootAttribute => $target->getAttribute($this->rootAttribute)], $db->quoteColumnName($this->leftAttribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($this->rightAttribute) . '<=' . $right . ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute, [':' . $this->rootAttribute => $this->owner->getAttribute($this->rootAttribute)]); $this->shiftLeftRight($right + 1, $left - $right - 1); if (isset($transaction)) { $transaction->commit(); } $this->correctCachedOnMoveBetweenTrees($key, $levelDelta, $target->getAttribute($this->rootAttribute)); } else { $delta = $right - $left + 1; $this->shiftLeftRight($key, $delta); if ($left >= $key) { $left += $delta; $right += $delta; } $condition = $db->quoteColumnName($this->leftAttribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($this->rightAttribute) . '<=' . $right; $params = []; if ($this->hasManyRoots) { $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute; $params[':' . $this->rootAttribute] = $this->owner->getAttribute($this->rootAttribute); } $updateColumns = []; $updateColumns[$this->levelAttribute] = new Expression($db->quoteColumnName($this->levelAttribute) . sprintf('%+d', $levelDelta)); if ($this->hasPaths && $this->owner->hasAttribute($this->pathAttribute)) { $pathLength = Tools::strlen($this->_previousPath) + 1; // SQL Server: SUBSTRING() rather than SUBSTR // SQL Server: + instead of CONCAT if ($db->getDriverName() == 'mssql') { $updateColumns[$this->pathAttribute] = new Expression($db->quoteValue($this->owner->getAttribute($this->pathAttribute)) . ' + SUBSTRING(' . $db->quoteColumnName($this->pathAttribute) . ', ' . $pathLength . '))'); } else { $updateColumns[$this->pathAttribute] = new Expression('CONCAT(' . $db->quoteValue($this->owner->getAttribute($this->pathAttribute)) . ', SUBSTR(' . $db->quoteColumnName($this->pathAttribute) . ', ' . $pathLength . '))'); } } $this->owner->updateAll($updateColumns, $condition, $params); foreach ([$this->leftAttribute, $this->rightAttribute] as $attribute) { $condition = $db->quoteColumnName($attribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($attribute) . '<=' . $right; $params = []; if ($this->hasManyRoots) { $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute; $params[':' . $this->rootAttribute] = $this->owner->getAttribute($this->rootAttribute); } $this->owner->updateAll([$attribute => new Expression($db->quoteColumnName($attribute) . sprintf('%+d', $key - $left))], $condition, $params); } $this->shiftLeftRight($right + 1, -$delta); $result = $this->afterMoveNode($this->_previousPath); if (isset($transaction)) { if ($result) { $transaction->commit(); } else { $transaction->rollback(); $this->_previousPath = ''; return false; } } $this->correctCachedOnMoveNode($key, $levelDelta); } } catch (\Exception $e) { if (isset($transaction)) { $transaction->rollback(); } throw $e; } $this->_previousPath = ''; return true; }
/** * Inlucde a toastr ajax response * * @param string $title * @param string $message * @param ActiveRecord $model * @param string $type [optional] 'success', 'info', 'warning' or 'error'. default is 'info' * @param array $options [optional] override default toast options * closeButton: true, * positionClass: 'toast-top-right', * onclick: null, * showDuration: 1000, * hideDuration: 1000, * timeOut: 5000, * extendedTimeOut: 1000, * showEasing: 'swing', * hideEasing: 'linear', * showMethod: 'fadeIn', * hideMethod: 'fadeOut', */ public function addAjaxToastResponse($title, $message, $type = 'info', $model = null, $options = []) { $message = $message ? Html::encode($message) : ''; if ($model !== null) { if ($model instanceof Model && $model->hasErrors()) { $errors = $model->getFirstErrors(); foreach ($errors as $error) { $message .= ($message != '' ? '<br/>' : '') . Html::encode($error); } } if ($model instanceof ActiveRecord && $model->hasActionErrors()) { $errors = $model->getBasicActionErrors(); foreach ($errors as $error) { $message .= ($message != '' ? '<br/>' : '') . Html::encode($error); } } } if ($options) { foreach ($options as $k => $option) { $options[$k] = Html::encode($option); } } $this->addAjaxJSResponse("Main.newToast('" . Html::encode($type) . "', '" . Html::encode($title) . "', '" . $message . "', '" . Json::encode($options) . "');"); }