diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index 923a78499..44e8e8806 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -8249,7 +8249,6 @@ label { background: transparent; border-color: transparent; height: auto; - /*min-height: 38px;*/ } .field-recordfinder .btn { position: absolute; @@ -8257,8 +8256,7 @@ label { top: 50%; margin-top: -44px; height: 88px; - width: 46px; - text-align: center; + width: 36px; } .field-recordfinder .btn:before { content: ''; @@ -8267,9 +8265,9 @@ label { height: 0; border-top: 44px solid transparent; border-bottom: 44px solid transparent; - border-right: 36px solid #e3e3e3; + border-right: 26px solid #e3e3e3; position: absolute; - left: -26px; + left: -16px; top: 50%; margin-top: -44px; } @@ -8283,7 +8281,7 @@ label { height: 0; border-top: 44px solid transparent; border-bottom: 44px solid transparent; - border-right: 36px solid #cfcfcf; + border-right: 26px solid #cfcfcf; } .form-buttons { padding-bottom: 20px; diff --git a/modules/backend/assets/less/controls/forms.less b/modules/backend/assets/less/controls/forms.less index 003ce174d..21c46a81d 100644 --- a/modules/backend/assets/less/controls/forms.less +++ b/modules/backend/assets/less/controls/forms.less @@ -200,7 +200,6 @@ label { background: transparent; border-color: transparent; height: auto; - /*min-height: 38px;*/ } .btn { position: absolute; @@ -208,12 +207,11 @@ label { top: 50%; margin-top: -44px; height: 88px; - width: 46px; - text-align: center; + width: 36px; &:before { - .triangle(left, 36px, 88px, @btn-default-bg); + .triangle(left, 26px, 88px, @btn-default-bg); position: absolute; - left: -26px; + left: -16px; top: 50%; margin-top: -44px; } @@ -222,7 +220,7 @@ label { &:active, &.active { &:before { - .triangle(left, 36px, 88px, darken(@btn-default-bg, 8%)); + .triangle(left, 26px, 88px, darken(@btn-default-bg, 8%)); } } } diff --git a/modules/backend/behaviors/FormController.php b/modules/backend/behaviors/FormController.php index 2315e9e5f..ed141076a 100644 --- a/modules/backend/behaviors/FormController.php +++ b/modules/backend/behaviors/FormController.php @@ -87,6 +87,14 @@ class FormController extends ControllerBehavior $this->controller->formExtendFields($host); }); + $this->formWidget->bindEvent('form.refreshBefore', function($host, $saveData) { + return $this->controller->formExtendRefreshData($host, $saveData); + }); + + $this->formWidget->bindEvent('form.refresh', function($host, $result) { + return $this->controller->formExtendRefreshResults($host, $result); + }); + $this->formWidget->bindToController(); /* @@ -509,6 +517,22 @@ class FormController extends ControllerBehavior */ public function formExtendFields($host) {} + /** + * Called before the form is refreshed, should return an array of additional save data. + * @param Backend\Widgets\Form $host The hosting form widget + * @param array $saveData Current save data + * @return array + */ + public function formExtendRefreshData($host, $saveData) {} + + /** + * Called after the form is refreshed, should return an array of additional result parameters. + * @param Backend\Widgets\Form $host The hosting form widget + * @param array $result Current result parameters. + * @return array + */ + public function formExtendRefreshResults($host, $result) {} + /** * Extend supplied model used by create and update actions, the model can * be altered by overriding it in the controller. diff --git a/modules/backend/behaviors/ListController.php b/modules/backend/behaviors/ListController.php index bae1cb888..0a96c8c5e 100644 --- a/modules/backend/behaviors/ListController.php +++ b/modules/backend/behaviors/ListController.php @@ -164,7 +164,7 @@ class ListController extends ControllerBehavior if ($searchWidget = $toolbarWidget->getSearchWidget()) { $searchWidget->bindEvent('search.submit', function() use ($widget, $searchWidget) { $widget->setSearchTerm($searchWidget->getActiveTerm()); - return $widget->onRender(); + return $widget->onRefresh(); }); // Find predefined search term @@ -224,7 +224,7 @@ class ListController extends ControllerBehavior if (!$definition || !isset($this->listDefinitions[$definition])) $definition = $this->primaryDefinition; - return $this->listWidgets[$definition]->onRender(); + return $this->listWidgets[$definition]->onRefresh(); } // diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php index 2e072068a..68b875f37 100644 --- a/modules/backend/classes/Controller.php +++ b/modules/backend/classes/Controller.php @@ -401,6 +401,9 @@ class Controller extends Extendable */ $this->pageAction(); + if ($this->fatalError) + throw new SystemException($this->fatalError); + if (!isset($this->widget->{$widgetName})) throw new SystemException(Lang::get('backend::lang.widget.not_bound', ['name'=>$widgetName])); diff --git a/modules/backend/formwidgets/RecordFinder.php b/modules/backend/formwidgets/RecordFinder.php index 1eebc7240..86349931b 100644 --- a/modules/backend/formwidgets/RecordFinder.php +++ b/modules/backend/formwidgets/RecordFinder.php @@ -11,8 +11,8 @@ use Backend\Classes\FormWidgetBase; * type: recordfinder * list: @/plugins/rainlab/user/models/user/columns.yaml * prompt: Click the Find button to find a user - * displayName: name - * displayDescription: email + * nameColumn: name + * descriptionColumn: email * * @package october\backend * @author Alexey Bobkov, Samuel Georges @@ -24,23 +24,65 @@ class RecordFinder extends FormWidgetBase */ public $defaultAlias = 'recordfinder'; + /** + * @var string Relationship type + */ + public $relationType; + + /** + * @var string Relationship name + */ + public $relationName; + + /** + * @var Model Relationship model + */ + public $relationModel; + + /** + * @var string Field name to use for key. + */ + public $keyField = 'id'; + /** * @var string Relation column to display for the name */ - public $displayName; + public $nameColumn; /** * @var string Relation column to display for the description */ - public $displayDescription; + public $descriptionColumn; + + /** + * @var string Prompt to display if no record is selected. + */ + public $prompt; + + /** + * @var Backend\Classes\WidgetBase Reference to the widget used for viewing (list or form). + */ + protected $listWidget; /** * {@inheritDoc} */ public function init() { - $this->displayName = $this->getConfig('displayName', $this->displayName); - $this->displayDescription = $this->getConfig('displayDescription', $this->displayDescription); + $this->relationName = $this->formField->columnName; + $this->relationType = $this->model->getRelationType($this->relationName); + + $this->prompt = $this->getConfig('prompt', 'Click the %s button to find a record'); + $this->keyField = $this->getConfig('keyField', $this->keyField); + $this->nameColumn = $this->getConfig('nameColumn', $this->nameColumn); + $this->descriptionColumn = $this->getConfig('descriptionColumn', $this->descriptionColumn); + + if (!$this->model->hasRelation($this->relationName)) + throw new SystemException(Lang::get('backend::lang.model.missing_relation', ['class'=>get_class($this->controller), 'relation'=>$this->relationName])); + + if (post('recordfinder_flag')) { + $this->listWidget = $this->makeListWidget(); + } } /** @@ -52,18 +94,25 @@ class RecordFinder extends FormWidgetBase return $this->makePartial('container'); } + public function onRefresh() + { + $this->model->{$this->columnName} = post($this->formField->getName()); + $this->prepareVars(); + return ['#'.$this->getId('container') => $this->makePartial('recordfinder')]; + } + /** * Prepares the list data */ public function prepareVars() { - $this->vars['name'] = $this->formField->getName(); - - $value = $this->model->{$this->columnName}; - - $this->vars['value'] = $value ?: ''; - $this->vars['displayName'] = $this->displayName; - $this->vars['displayDescription'] = $this->displayDescription; + $this->relationModel = $this->model->{$this->columnName}; + $this->vars['value'] = $this->getKeyValue(); + $this->vars['field'] = $this->formField; + $this->vars['nameValue'] = $this->getNameValue(); + $this->vars['descriptionValue'] = $this->getDescriptionValue(); + $this->vars['listWidget'] = $this->listWidget; + $this->vars['prompt'] = str_replace('%s', '', $this->prompt); } /** @@ -71,7 +120,7 @@ class RecordFinder extends FormWidgetBase */ public function loadAssets() { - + $this->addJs('js/recordfinder.js', 'core'); } /** @@ -79,6 +128,61 @@ class RecordFinder extends FormWidgetBase */ public function getSaveData($value) { - return $value; + return strlen($value) ? $value : null; + } + + public function getKeyValue() + { + if (!$this->relationModel) + return null; + + return $this->relationModel->{$this->keyField}; + } + + public function getNameValue() + { + if (!$this->relationModel || !$this->nameColumn) + return null; + + return $this->relationModel->{$this->nameColumn}; + } + + public function getDescriptionValue() + { + if (!$this->relationModel || !$this->descriptionColumn) + return null; + + return $this->relationModel->{$this->descriptionColumn}; + } + + public function onFindRecord() + { + $this->prepareVars(); + return $this->makePartial('recordfinder_form'); + } + + protected function makeListWidget() + { + $config = $this->makeConfig($this->getConfig('list')); + $config->model = $this->model->makeRelation($this->relationName); + $config->alias = $this->alias . 'List'; + $config->showSetup = false; + $config->showCheckboxes = false; + $config->recordOnClick = sprintf("$('#%s').recordFinder('updateRecord', this, ':id')", $this->getId()); + $widget = $this->makeWidget('Backend\Widgets\Lists', $config); + + // $widget->bindEvent('list.extendQueryBefore', function($host, $query) { + + // /* + // * Where not in the current list of related records + // */ + // $existingIds = $this->findExistingRelationIds(); + // if (count($existingIds)) { + // $query->whereNotIn('id', $existingIds); + // } + + // }); + + return $widget; } } \ No newline at end of file diff --git a/modules/backend/formwidgets/Relation.php b/modules/backend/formwidgets/Relation.php index b1e4e204b..728844e60 100644 --- a/modules/backend/formwidgets/Relation.php +++ b/modules/backend/formwidgets/Relation.php @@ -86,34 +86,34 @@ class Relation extends FormWidgetBase */ protected function makeRenderFormField() { - $field = clone $this->formField; - $relationObj = $this->model->{$this->relationName}(); - $relatedObj = $this->model->makeRelation($this->relationName); - $query = $relatedObj->newQuery(); + $field = clone $this->formField; + $relationObj = $this->model->{$this->relationName}(); + $relatedObj = $this->model->makeRelation($this->relationName); + $query = $relatedObj->newQuery(); - if (in_array($this->relationType, ['belongsToMany', 'morphToMany', 'morphedByMany'])) { + if (in_array($this->relationType, ['belongsToMany', 'morphToMany', 'morphedByMany'])) { $field->type = 'checkboxlist'; $field->value = $relationObj->getRelatedIds(); - } - else if ($this->relationType == 'belongsTo') { + } + else if ($this->relationType == 'belongsTo') { $field->type = 'dropdown'; $field->placeholder = $this->emptyOption; $foreignKey = $relationObj->getForeignKey(); $field->value = $this->model->$foreignKey; - } - - // It is safe to assume that if the model and related model are of - // the exact same class, then it cannot be related to itself - if ($this->model->exists && (get_class($this->model) == get_class($relatedObj))) { - $query->where($relatedObj->getKeyName(), '<>', $this->model->id); - } + } - if (in_array('October\Rain\Database\Traits\NestedTree', class_uses($relatedObj))) + // It is safe to assume that if the model and related model are of + // the exact same class, then it cannot be related to itself + if ($this->model->exists && (get_class($this->model) == get_class($relatedObj))) { + $query->where($relatedObj->getKeyName(), '<>', $this->model->id); + } + + if (in_array('October\Rain\Database\Traits\NestedTree', class_uses($relatedObj))) $field->options = $query->listsNested($this->nameColumn, $relatedObj->getKeyName()); - else + else $field->options = $query->lists($this->nameColumn, $relatedObj->getKeyName()); - return $this->renderFormField = $field; + return $this->renderFormField = $field; } /** diff --git a/modules/backend/formwidgets/datagrid/partials/_datagrid.htm b/modules/backend/formwidgets/datagrid/partials/_datagrid.htm index 33d16d8bc..f8cb23918 100644 --- a/modules/backend/formwidgets/datagrid/partials/_datagrid.htm +++ b/modules/backend/formwidgets/datagrid/partials/_datagrid.htm @@ -9,13 +9,13 @@ style="width:100%" class="control-datagrid" data-control="datagrid" - data-data-locker="#= $this->getId('datalocker') ?>" + data-data-locker="#= $this->getId('dataLocker') ?>" data-autocomplete-handler="= $this->getEventHandler('onAutocomplete') ?>" > diff --git a/modules/backend/formwidgets/recordfinder/assets/js/recordfinder.js b/modules/backend/formwidgets/recordfinder/assets/js/recordfinder.js new file mode 100644 index 000000000..d872e5a4a --- /dev/null +++ b/modules/backend/formwidgets/recordfinder/assets/js/recordfinder.js @@ -0,0 +1,81 @@ +/* + * RecordFinder plugin + * + * Data attributes: + * - data-control="recordfinder" - enables the plugin on an element + * - data-option="value" - an option with a value + * + * JavaScript API: + * $('a#someElement').recordFinder({ option: 'value' }) + * + * Dependences: + * - Some other plugin (filename.js) + */ + ++function ($) { "use strict"; + + // RECORDFINDER CLASS DEFINITION + // ============================ + + var RecordFinder = function(element, options) { + var self = this + this.options = options + this.$el = $(element) + } + + RecordFinder.DEFAULTS = { + refreshHandler: null, + dataLocker: null + } + + RecordFinder.prototype.updateRecord = function(linkEl, recordId) { + if (!this.options.dataLocker) return + var self = this + $(this.options.dataLocker).val(recordId) + + this.$el.request(this.options.refreshHandler, { + success: function(data) { + this.success(data) + $(self.options.dataLocker).trigger('change') + } + }) + + $(linkEl).closest('.recordfinder-popup').popup('hide') + } + + // RECORDFINDER PLUGIN DEFINITION + // ============================ + + var old = $.fn.recordFinder + + $.fn.recordFinder = function (option) { + var args = Array.prototype.slice.call(arguments, 1), result + this.each(function () { + var $this = $(this) + var data = $this.data('oc.recordfinder') + var options = $.extend({}, RecordFinder.DEFAULTS, $this.data(), typeof option == 'object' && option) + if (!data) $this.data('oc.recordfinder', (data = new RecordFinder(this, options))) + if (typeof option == 'string') result = data[option].apply(data, args) + if (typeof result != 'undefined') return false + }) + + return result ? result : this + } + + $.fn.recordFinder.Constructor = RecordFinder + + // RECORDFINDER NO CONFLICT + // ================= + + $.fn.recordFinder.noConflict = function () { + $.fn.recordFinder = old + return this + } + + // RECORDFINDER DATA-API + // =============== + $(document).render(function () { + $('[data-control="recordfinder"]').recordFinder() + }) + +}(window.jQuery); diff --git a/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm b/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm index 27ff85366..8f6431a63 100644 --- a/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm +++ b/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm @@ -1,7 +1,32 @@ -