From aa7f831a4e79bdb04ef81f1d3234b3f9779a6a46 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Fri, 27 Mar 2015 17:36:28 +1100 Subject: [PATCH 01/27] Fixes a conflict with triggerapi and popup scripts The hide event wasn't namespaced and caused the backdrop to hide --- modules/backend/assets/js/october-min.js | 10 +++++----- modules/backend/assets/js/october.triggerapi.js | 13 +++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/backend/assets/js/october-min.js b/modules/backend/assets/js/october-min.js index d9ec92aad..0b806275d 100644 --- a/modules/backend/assets/js/october-min.js +++ b/modules/backend/assets/js/october-min.js @@ -89,15 +89,15 @@ self.onConditionChanged()} TriggerOn.prototype.onConditionChanged=function(){if(this.triggerCondition=='checked'){this.updateTarget($(this.options.trigger+':checked',this.triggerParent).length>0)} else if(this.triggerCondition=='value'){var trigger=$(this.options.trigger+':checked',this.triggerParent);if(trigger.length){this.updateTarget(trigger.val()==this.triggerConditionValue)}else{this.updateTarget($(this.options.trigger,this.triggerParent).val()==this.triggerConditionValue)}}} TriggerOn.prototype.updateTarget=function(status){if(this.options.triggerAction=='show') -this.$el.toggleClass('hide',!status).trigger('hide',[!status]) +this.$el.toggleClass('hide',!status).trigger('hide.oc.triggerapi',[!status]) else if(this.options.triggerAction=='hide') -this.$el.toggleClass('hide',status).trigger('hide',[status]) +this.$el.toggleClass('hide',status).trigger('hide.oc.triggerapi',[status]) else if(this.options.triggerAction=='enable') -this.$el.prop('disabled',!status).trigger('disable',[!status]).toggleClass('control-disabled',!status) +this.$el.prop('disabled',!status).trigger('disable.oc.triggerapi',[!status]).toggleClass('control-disabled',!status) else if(this.options.triggerAction=='disable') -this.$el.prop('disabled',status).trigger('disable',[status]).toggleClass('control-disabled',status) +this.$el.prop('disabled',status).trigger('disable.oc.triggerapi',[status]).toggleClass('control-disabled',status) else if(this.options.triggerAction=='empty'&&status) -this.$el.trigger('empty').val('') +this.$el.trigger('empty.oc.triggerapi').val('') if(this.options.triggerAction=='show'||this.options.triggerAction=='hide') this.fixButtonClasses() $(window).trigger('resize')} diff --git a/modules/backend/assets/js/october.triggerapi.js b/modules/backend/assets/js/october.triggerapi.js index c0be46ff8..f9af7a5a9 100644 --- a/modules/backend/assets/js/october.triggerapi.js +++ b/modules/backend/assets/js/october.triggerapi.js @@ -81,7 +81,8 @@ var trigger = $(this.options.trigger + ':checked', this.triggerParent); if (trigger.length) { this.updateTarget(trigger.val() == this.triggerConditionValue) - } else { + } + else { this.updateTarget($(this.options.trigger, this.triggerParent).val() == this.triggerConditionValue) } } @@ -89,15 +90,15 @@ TriggerOn.prototype.updateTarget = function(status) { if (this.options.triggerAction == 'show') - this.$el.toggleClass('hide', !status).trigger('hide', [!status]) + this.$el.toggleClass('hide', !status).trigger('hide.oc.triggerapi', [!status]) else if (this.options.triggerAction == 'hide') - this.$el.toggleClass('hide', status).trigger('hide', [status]) + this.$el.toggleClass('hide', status).trigger('hide.oc.triggerapi', [status]) else if (this.options.triggerAction == 'enable') - this.$el.prop('disabled', !status).trigger('disable', [!status]).toggleClass('control-disabled', !status) + this.$el.prop('disabled', !status).trigger('disable.oc.triggerapi', [!status]).toggleClass('control-disabled', !status) else if (this.options.triggerAction == 'disable') - this.$el.prop('disabled', status).trigger('disable', [status]).toggleClass('control-disabled', status) + this.$el.prop('disabled', status).trigger('disable.oc.triggerapi', [status]).toggleClass('control-disabled', status) else if (this.options.triggerAction == 'empty' && status) - this.$el.trigger('empty').val('') + this.$el.trigger('empty.oc.triggerapi').val('') if (this.options.triggerAction == 'show' || this.options.triggerAction == 'hide') this.fixButtonClasses() From 3eede4a7b774b104b384f3b27cc338c0eff162c3 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Fri, 27 Mar 2015 22:20:52 +1100 Subject: [PATCH 02/27] Reorganise and improve RelationController Fix bug in DataTable limited to 10 records --- .../backend/behaviors/RelationController.php | 825 +++++++++--------- .../assets/js/october.relation.js | 28 +- .../partials/_manage_pivot.htm | 12 + modules/backend/formwidgets/DataTable.php | 2 +- .../widgets/lists/assets/js/october.list.js | 10 +- 5 files changed, 450 insertions(+), 427 deletions(-) diff --git a/modules/backend/behaviors/RelationController.php b/modules/backend/behaviors/RelationController.php index ac10a4d98..b09c7e37f 100644 --- a/modules/backend/behaviors/RelationController.php +++ b/modules/backend/behaviors/RelationController.php @@ -187,6 +187,69 @@ class RelationController extends ControllerBehavior $this->config = $this->originalConfig = $this->makeConfig($controller->relationConfig, $this->requiredConfig); } + /** + * Validates the supplied field and initializes the relation manager. + * @param string $field The relationship field. + * @return string The active field name. + */ + protected function validateField($field = null) + { + $field = $field ?: post(self::PARAM_FIELD); + + if ($field && $field != $this->field) { + $this->initRelation($this->model, $field); + } + + if (!$field && !$this->field) { + throw new ApplicationException(Lang::get('backend::lang.relation.missing_definition', compact('field'))); + } + + return $field ?: $this->field; + } + + /** + * Prepares the view data. + * @return void + */ + public function prepareVars() + { + $this->vars['relationManageId'] = $this->manageId; + $this->vars['relationLabel'] = $this->config->label ?: $this->field; + $this->vars['relationField'] = $this->field; + $this->vars['relationType'] = $this->relationType; + $this->vars['relationSearchWidget'] = $this->searchWidget; + $this->vars['relationToolbarWidget'] = $this->toolbarWidget; + $this->vars['relationManageMode'] = $this->manageMode; + $this->vars['relationManageWidget'] = $this->manageWidget; + $this->vars['relationToolbarButtons'] = $this->toolbarButtons; + $this->vars['relationViewMode'] = $this->viewMode; + $this->vars['relationViewWidget'] = $this->viewWidget; + $this->vars['relationViewModel'] = $this->viewModel; + $this->vars['relationPivotWidget'] = $this->pivotWidget; + $this->vars['relationSessionKey'] = $this->relationGetSessionKey(); + } + + /** + * The controller action is responsible for supplying the parent model + * so it's action must be fired. Additionally, each AJAX request must + * supply the relation's field name (_relation_field). + */ + protected function beforeAjax() + { + if ($this->initialized) { + return; + } + + $this->controller->pageAction(); + $this->validateField(); + $this->prepareVars(); + $this->initialized = true; + } + + // + // Interface + // + /** * Prepare the widgets used by this behavior * @param Model $model @@ -354,65 +417,6 @@ class RelationController extends ControllerBehavior return $this->relationRender($field, ['section' => 'view']); } - /** - * Validates the supplied field and initializes the relation manager. - * @param string $field The relationship field. - * @return string The active field name. - */ - protected function validateField($field = null) - { - $field = $field ?: post(self::PARAM_FIELD); - - if ($field && $field != $this->field) { - $this->initRelation($this->model, $field); - } - - if (!$field && !$this->field) { - throw new ApplicationException(Lang::get('backend::lang.relation.missing_definition', compact('field'))); - } - - return $field ?: $this->field; - } - - /** - * Prepares the view data. - * @return void - */ - public function prepareVars() - { - $this->vars['relationManageId'] = $this->manageId; - $this->vars['relationLabel'] = $this->config->label ?: $this->field; - $this->vars['relationField'] = $this->field; - $this->vars['relationType'] = $this->relationType; - $this->vars['relationSearchWidget'] = $this->searchWidget; - $this->vars['relationToolbarWidget'] = $this->toolbarWidget; - $this->vars['relationManageMode'] = $this->manageMode; - $this->vars['relationManageWidget'] = $this->manageWidget; - $this->vars['relationToolbarButtons'] = $this->toolbarButtons; - $this->vars['relationViewMode'] = $this->viewMode; - $this->vars['relationViewWidget'] = $this->viewWidget; - $this->vars['relationViewModel'] = $this->viewModel; - $this->vars['relationPivotWidget'] = $this->pivotWidget; - $this->vars['relationSessionKey'] = $this->relationGetSessionKey(); - } - - /** - * The controller action is responsible for supplying the parent model - * so it's action must be fired. Additionally, each AJAX request must - * supply the relation's field name (_relation_field). - */ - protected function beforeAjax() - { - if ($this->initialized) { - return; - } - - $this->controller->pageAction(); - $this->validateField(); - $this->prepareVars(); - $this->initialized = true; - } - /** * Controller accessor for making partials within this behavior. * @param string $partial @@ -449,39 +453,317 @@ class RelationController extends ControllerBehavior } /** - * Returns the existing record IDs for the relation. + * Returns the active session key. */ - protected function findExistingRelationIds($checkIds = null) + public function relationGetSessionKey($force = false) { - $foreignKeyName = $this->relationModel->getQualifiedKeyName(); - - $results = $this->relationObject - ->getBaseQuery() - ->select($foreignKeyName); - - if ($checkIds !== null && is_array($checkIds) && count($checkIds)) { - $results = $results->whereIn($foreignKeyName, $checkIds); + if ($this->sessionKey && !$force) { + return $this->sessionKey; } - return $results->lists($foreignKeyName); + if (post('_relation_session_key')) { + return $this->sessionKey = post('_relation_session_key'); + } + + if (post('_session_key')) { + return $this->sessionKey = post('_session_key'); + } + + return $this->sessionKey = FormHelper::getSessionKey(); } // - // Overrides + // Widgets // - /** - * Controller override: Extend the query used for populating the list - * after the default query is processed. - * @param October\Rain\Database\Builder $query - * @param string $field - */ - public function relationExtendQuery($query, $field) + protected function makeSearchWidget() { + $config = $this->makeConfig(); + $config->alias = $this->alias . 'ManageSearch'; + $config->growable = false; + $config->prompt = 'backend::lang.list.search_prompt'; + $widget = $this->makeWidget('Backend\Widgets\Search', $config); + $widget->cssClasses[] = 'recordfinder-search'; + return $widget; } - public function relationExtendRefreshResults($field) + protected function makeToolbarWidget() { + $defaultConfig = []; + + /* + * Add buttons to toolbar + */ + $defaultButtons = null; + + if (!$this->readOnly) { + $defaultButtons = '~/modules/backend/behaviors/relationcontroller/partials/_toolbar.htm'; + } + + $defaultConfig['buttons'] = $this->getConfig('view[toolbarPartial]', $defaultButtons); + + /* + * Make config + */ + $toolbarConfig = $this->makeConfig($this->getConfig('toolbar', $defaultConfig)); + $toolbarConfig->alias = $this->alias . 'Toolbar'; + + /* + * Add search to toolbar + */ + $useSearch = $this->viewMode == 'multi' && $this->getConfig('view[showSearch]'); + + if ($useSearch) { + $toolbarConfig->search = [ + 'prompt' => 'backend::lang.list.search_prompt' + ]; + } + + /* + * No buttons, no search should mean no toolbar + */ + if (empty($toolbarConfig->search) && empty($toolbarConfig->buttons)) + return; + + $toolbarWidget = $this->makeWidget('Backend\Widgets\Toolbar', $toolbarConfig); + $toolbarWidget->cssClasses[] = 'list-header'; + + return $toolbarWidget; + } + + protected function makeViewWidget() + { + /* + * Multiple (has many, belongs to many) + */ + if ($this->viewMode == 'multi') { + $config = $this->makeConfigForMode('view', 'list'); + $config->model = $this->relationModel; + $config->alias = $this->alias . 'ViewList'; + $config->showSorting = $this->getConfig('view[showSorting]', true); + $config->defaultSort = $this->getConfig('view[defaultSort]'); + $config->recordsPerPage = $this->getConfig('view[recordsPerPage]'); + $config->showCheckboxes = $this->getConfig('view[showCheckboxes]', !$this->readOnly); + + $defaultOnClick = sprintf( + "$.oc.relationBehavior.clickViewListRecord(':id', '%s', '%s')", + $this->field, + $this->relationGetSessionKey() + ); + + $config->recordOnClick = $this->getConfig('view[recordOnClick]', $defaultOnClick); + $config->recordUrl = $this->getConfig('view[recordUrl]', null); + + if ($emptyMessage = $this->getConfig('emptyMessage')) { + $config->noRecordsMessage = $emptyMessage; + } + + /* + * Constrain the query by the relationship and deferred items + */ + $widget = $this->makeWidget('Backend\Widgets\Lists', $config); + $widget->bindEvent('list.extendQuery', function ($query) { + $this->controller->relationExtendQuery($query, $this->field); + + $this->relationObject->setQuery($query); + if ($sessionKey = $this->relationGetSessionKey()) { + $this->relationObject->withDeferred($sessionKey); + } + elseif ($this->model->exists) { + $this->relationObject->addConstraints(); + } + + /* + * Allows pivot data to enter the fray + */ + if ($this->relationType == 'belongsToMany') { + $this->relationObject->setQuery($query->getQuery()); + return $this->relationObject; + } + }); + + /* + * Constrain the list by the search widget, if available + */ + if ($this->toolbarWidget && $this->getConfig('view[showSearch]')) { + if ($searchWidget = $this->toolbarWidget->getSearchWidget()) { + $searchWidget->bindEvent('search.submit', function () use ($widget, $searchWidget) { + $widget->setSearchTerm($searchWidget->getActiveTerm()); + return $widget->onRefresh(); + }); + + $searchWidget->setActiveTerm(null); + } + } + } + /* + * Single (belongs to, has one) + */ + elseif ($this->viewMode == 'single') { + $query = $this->relationObject; + $this->controller->relationExtendQuery($query, $this->field); + $this->viewModel = $query->getResults() ?: $this->relationModel; + + $config = $this->makeConfigForMode('view', 'form'); + $config->model = $this->viewModel; + $config->arrayName = class_basename($this->relationModel); + $config->context = 'relation'; + $config->alias = $this->alias . 'ViewForm'; + + $widget = $this->makeWidget('Backend\Widgets\Form', $config); + $widget->previewMode = true; + } + + return $widget; + } + + protected function makeManageWidget() + { + $widget = null; + + /* + * List / Pivot + */ + if ($this->manageMode == 'list' || $this->manageMode == 'pivot') { + $isPivot = $this->manageMode == 'pivot'; + + $config = $this->makeConfigForMode('manage', 'list'); + $config->model = $this->relationModel; + $config->alias = $this->alias . 'ManageList'; + $config->showSetup = false; + $config->showCheckboxes = $this->getConfig('manage[showCheckboxes]', !$isPivot); + $config->showSorting = $this->getConfig('manage[showSorting]', !$isPivot); + $config->defaultSort = $this->getConfig('manage[defaultSort]'); + $config->recordsPerPage = $this->getConfig('manage[recordsPerPage]'); + + if ($this->viewMode == 'single') { + $config->showCheckboxes = false; + $config->recordOnClick = sprintf( + "$.oc.relationBehavior.clickManageListRecord(:id, '%s', '%s')", + $this->field, + $this->relationGetSessionKey() + ); + } + elseif ($config->showCheckboxes) { + $config->recordOnClick = "$.oc.relationBehavior.toggleListCheckbox(this)"; + } + elseif ($isPivot) { + $config->recordOnClick = sprintf( + "$.oc.relationBehavior.clickManagePivotListRecord(:id, '%s', '%s')", + $this->field, + $this->relationGetSessionKey() + ); + } + + $widget = $this->makeWidget('Backend\Widgets\Lists', $config); + + /* + * Link the Search Widget to the List Widget + */ + if ($this->getConfig('manage[showSearch]')) { + $this->searchWidget = $this->makeSearchWidget(); + $this->searchWidget->bindToController(); + $this->searchWidget->bindEvent('search.submit', function () use ($widget) { + $widget->setSearchTerm($this->searchWidget->getActiveTerm()); + return $widget->onRefresh(); + }); + + $this->searchWidget->setActiveTerm(null); + } + } + /* + * Form + */ + elseif ($this->manageMode == 'form') { + + /* + * Determine supplied form context + */ + $manageConfig = isset($this->config->manage) ? $this->config->manage : []; + + if ($context = array_get($manageConfig, 'context')) { + if (is_array($context)) { + $context = $this->manageId + ? array_get($context, 'update') + : array_get($context, 'create'); + } + } + + $config = $this->makeConfigForMode('manage', 'form'); + $config->model = $this->relationModel; + $config->arrayName = class_basename($this->relationModel); + $config->context = $context ?: 'relation'; + $config->alias = $this->alias . 'ManageForm'; + + /* + * Existing record + */ + if ($this->manageId) { + $config->model = $config->model->find($this->manageId); + if (!$config->model) { + throw new ApplicationException(Lang::get('backend::lang.model.not_found', [ + 'class' => get_class($config->model), 'id' => $this->manageId + ])); + } + } + + $widget = $this->makeWidget('Backend\Widgets\Form', $config); + } + + if (!$widget) { + return null; + } + + /* + * Exclude existing relationships + */ + if ($this->manageMode == 'pivot' || $this->manageMode == 'list') { + $widget->bindEvent('list.extendQuery', function ($query) { + $this->controller->relationExtendQuery($query, $this->field); + + /* + * Where not in the current list of related records + */ + $existingIds = $this->findExistingRelationIds(); + if (count($existingIds)) { + $query->whereNotIn($this->relationModel->getQualifiedKeyName(), $existingIds); + } + + }); + } + + return $widget; + } + + protected function makePivotWidget() + { + $config = $this->makeConfigForMode('pivot', 'form'); + $config->model = $this->relationModel; + $config->arrayName = class_basename($this->relationModel); + $config->context = 'relation'; + $config->alias = $this->alias . 'ManagePivotForm'; + + /* + * Existing record + */ + if ($this->manageId) { + $foreignKeyName = $this->relationModel->getQualifiedKeyName(); + $hydratedModel = $this->relationObject->where($foreignKeyName, $this->manageId)->first(); + + $config->model = $hydratedModel; + if (!$config->model) { + throw new ApplicationException(Lang::get('backend::lang.model.not_found', [ + 'class' => get_class($config->model), 'id' => $this->manageId + ])); + } + } + else { + $pivotModel = $this->relationObject->newPivot(); + $config->model->setRelation('pivot', $pivotModel); + } + + $widget = $this->makeWidget('Backend\Widgets\Form', $config); + return $widget; } // @@ -775,11 +1057,19 @@ class RelationController extends ControllerBehavior return $this->relationRefresh(); } + /** + * Add multiple items using a single pivot form. + */ + public function onRelationManageAddPivot() + { + return $this->onRelationManagePivotForm(); + } + public function onRelationManagePivotForm() { $this->beforeAjax(); - $this->vars['foreignId'] = post('foreign_id'); + $this->vars['foreignId'] = post('foreign_id', post('checked')); return $this->relationMakePartial('pivot_form'); } @@ -787,18 +1077,26 @@ class RelationController extends ControllerBehavior { $this->beforeAjax(); - $foreignId = post('foreign_id'); - $foreignModel = $this->relationModel->find($foreignId); - $saveData = $this->pivotWidget->getSaveData(); + $foreignIds = (array) post('foreign_id'); + foreach ($foreignIds as $foreignId) { - /* - * Check for existing relation - */ - $foreignKeyName = $this->relationModel->getQualifiedKeyName(); - $existing = $this->relationObject->where($foreignKeyName, $foreignId)->count(); + /* + * Check for existing relation + */ + $foreignKeyName = $this->relationModel->getQualifiedKeyName(); + $existing = $this->relationObject->where($foreignKeyName, $foreignId)->count(); + + if (!$existing) { + /* + * Add related model to the parent model + */ + $saveData = $this->pivotWidget->getSaveData(); + $pivotData = array_get($saveData, 'pivot', []); + + $foreignModel = $this->relationModel->find($foreignId); + $this->relationObject->add($foreignModel, null, $pivotData); + } - if (!$existing) { - $this->relationObject->add($foreignModel, null, $saveData); } return ['#'.$this->relationGetId('view') => $this->relationRenderView()]; @@ -821,339 +1119,44 @@ class RelationController extends ControllerBehavior } // - // Widgets + // Overrides // - protected function makeSearchWidget() - { - $config = $this->makeConfig(); - $config->alias = $this->alias . 'ManageSearch'; - $config->growable = false; - $config->prompt = 'backend::lang.list.search_prompt'; - $widget = $this->makeWidget('Backend\Widgets\Search', $config); - $widget->cssClasses[] = 'recordfinder-search'; - return $widget; - } - - protected function makeToolbarWidget() - { - $defaultConfig = []; - - /* - * Add buttons to toolbar - */ - $defaultButtons = null; - - if (!$this->readOnly) { - $defaultButtons = '~/modules/backend/behaviors/relationcontroller/partials/_toolbar.htm'; - } - - $defaultConfig['buttons'] = $this->getConfig('view[toolbarPartial]', $defaultButtons); - - /* - * Make config - */ - $toolbarConfig = $this->makeConfig($this->getConfig('toolbar', $defaultConfig)); - $toolbarConfig->alias = $this->alias . 'Toolbar'; - - /* - * Add search to toolbar - */ - $useSearch = $this->viewMode == 'multi' && $this->getConfig('view[showSearch]'); - - if ($useSearch) { - $toolbarConfig->search = [ - 'prompt' => 'backend::lang.list.search_prompt' - ]; - } - - /* - * No buttons, no search should mean no toolbar - */ - if (empty($toolbarConfig->search) && empty($toolbarConfig->buttons)) - return; - - $toolbarWidget = $this->makeWidget('Backend\Widgets\Toolbar', $toolbarConfig); - $toolbarWidget->cssClasses[] = 'list-header'; - - return $toolbarWidget; - } - - protected function makeViewWidget() - { - /* - * Multiple (has many, belongs to many) - */ - if ($this->viewMode == 'multi') { - $config = $this->makeConfigForMode('view', 'list'); - $config->model = $this->relationModel; - $config->alias = $this->alias . 'ViewList'; - $config->showSorting = $this->getConfig('view[showSorting]', true); - $config->defaultSort = $this->getConfig('view[defaultSort]'); - $config->recordsPerPage = $this->getConfig('view[recordsPerPage]'); - $config->showCheckboxes = $this->getConfig('view[showCheckboxes]', !$this->readOnly); - - $defaultOnClick = sprintf( - "$.oc.relationBehavior.clickViewListRecord(':id', '%s', '%s')", - $this->field, - $this->relationGetSessionKey() - ); - - $config->recordOnClick = $this->getConfig('view[recordOnClick]', $defaultOnClick); - $config->recordUrl = $this->getConfig('view[recordUrl]', null); - - if ($emptyMessage = $this->getConfig('emptyMessage')) { - $config->noRecordsMessage = $emptyMessage; - } - - /* - * Constrain the query by the relationship and deferred items - */ - $widget = $this->makeWidget('Backend\Widgets\Lists', $config); - $widget->bindEvent('list.extendQuery', function ($query) { - $this->controller->relationExtendQuery($query, $this->field); - - $this->relationObject->setQuery($query); - if ($sessionKey = $this->relationGetSessionKey()) { - $this->relationObject->withDeferred($sessionKey); - } - elseif ($this->model->exists) { - $this->relationObject->addConstraints(); - } - - /* - * Allows pivot data to enter the fray - */ - if ($this->relationType == 'belongsToMany') { - $this->relationObject->setQuery($query->getQuery()); - return $this->relationObject; - } - }); - - /* - * Constrain the list by the search widget, if available - */ - if ($this->toolbarWidget && $this->getConfig('view[showSearch]')) { - if ($searchWidget = $this->toolbarWidget->getSearchWidget()) { - $searchWidget->bindEvent('search.submit', function () use ($widget, $searchWidget) { - $widget->setSearchTerm($searchWidget->getActiveTerm()); - return $widget->onRefresh(); - }); - - $searchWidget->setActiveTerm(null); - } - } - } - /* - * Single (belongs to, has one) - */ - elseif ($this->viewMode == 'single') { - $query = $this->relationObject; - $this->controller->relationExtendQuery($query, $this->field); - $this->viewModel = $query->getResults() ?: $this->relationModel; - - $config = $this->makeConfigForMode('view', 'form'); - $config->model = $this->viewModel; - $config->arrayName = class_basename($this->relationModel); - $config->context = 'relation'; - $config->alias = $this->alias . 'ViewForm'; - - $widget = $this->makeWidget('Backend\Widgets\Form', $config); - $widget->previewMode = true; - } - - return $widget; - } - - protected function makeManageWidget() - { - $widget = null; - /* - * Pivot - */ - if ($this->manageMode == 'pivot') { - $config = $this->makeConfigForMode('manage', 'list'); - $config->model = $this->relationModel; - $config->alias = $this->alias . 'ManagePivotList'; - $config->showSetup = false; - $config->showSorting = $this->getConfig('manage[showSorting]', false); - $config->defaultSort = $this->getConfig('manage[defaultSort]'); - $config->recordsPerPage = $this->getConfig('manage[recordsPerPage]'); - $config->recordOnClick = sprintf( - "$.oc.relationBehavior.clickManagePivotListRecord(:id, '%s', '%s')", - $this->field, - $this->relationGetSessionKey() - ); - - $widget = $this->makeWidget('Backend\Widgets\Lists', $config); - - /* - * Link the Search Widget to the List Widget - */ - if ($this->getConfig('pivot[showSearch]')) { - $this->searchWidget = $this->makeSearchWidget(); - $this->searchWidget->bindToController(); - $this->searchWidget->bindEvent('search.submit', function () use ($widget) { - $widget->setSearchTerm($this->searchWidget->getActiveTerm()); - return $widget->onRefresh(); - }); - - $this->searchWidget->setActiveTerm(null); - } - } - /* - * List - */ - elseif ($this->manageMode == 'list') { - $config = $this->makeConfigForMode('manage', 'list'); - $config->model = $this->relationModel; - $config->alias = $this->alias . 'ManageList'; - $config->showSetup = false; - $config->showCheckboxes = true; - $config->showSorting = $this->getConfig('manage[showSorting]', true); - $config->defaultSort = $this->getConfig('manage[defaultSort]'); - $config->recordsPerPage = $this->getConfig('manage[recordsPerPage]'); - - if ($this->viewMode == 'single') { - $config->showCheckboxes = false; - $config->recordOnClick = sprintf( - "$.oc.relationBehavior.clickManageListRecord(:id, '%s', '%s')", - $this->field, - $this->relationGetSessionKey() - ); - } - - $widget = $this->makeWidget('Backend\Widgets\Lists', $config); - - /* - * Link the Search Widget to the List Widget - */ - if ($this->getConfig('manage[showSearch]')) { - $this->searchWidget = $this->makeSearchWidget(); - $this->searchWidget->bindToController(); - $this->searchWidget->bindEvent('search.submit', function () use ($widget) { - $widget->setSearchTerm($this->searchWidget->getActiveTerm()); - return $widget->onRefresh(); - }); - - $this->searchWidget->setActiveTerm(null); - } - } - /* - * Form - */ - elseif ($this->manageMode == 'form') { - - /* - * Determine supplied form context - */ - $manageConfig = isset($this->config->manage) ? $this->config->manage : []; - - if ($context = array_get($manageConfig, 'context')) { - if (is_array($context)) { - $context = $this->manageId - ? array_get($context, 'update') - : array_get($context, 'create'); - } - } - - $config = $this->makeConfigForMode('manage', 'form'); - $config->model = $this->relationModel; - $config->arrayName = class_basename($this->relationModel); - $config->context = $context ?: 'relation'; - $config->alias = $this->alias . 'ManageForm'; - - /* - * Existing record - */ - if ($this->manageId) { - $config->model = $config->model->find($this->manageId); - if (!$config->model) { - throw new ApplicationException(Lang::get('backend::lang.model.not_found', [ - 'class' => get_class($config->model), 'id' => $this->manageId - ])); - } - } - - $widget = $this->makeWidget('Backend\Widgets\Form', $config); - } - - if (!$widget) { - return null; - } - - /* - * Exclude existing relationships - */ - if ($this->manageMode == 'pivot' || $this->manageMode == 'list') { - $widget->bindEvent('list.extendQuery', function ($query) { - $this->controller->relationExtendQuery($query, $this->field); - - /* - * Where not in the current list of related records - */ - $existingIds = $this->findExistingRelationIds(); - if (count($existingIds)) { - $query->whereNotIn($this->relationModel->getQualifiedKeyName(), $existingIds); - } - - }); - } - - return $widget; - } - - protected function makePivotWidget() - { - $config = $this->makeConfigForMode('pivot', 'form'); - $config->model = $this->relationModel; - $config->arrayName = class_basename($this->relationModel); - $config->context = 'relation'; - $config->alias = $this->alias . 'ManagePivotForm'; - - /* - * Existing record - */ - if ($this->manageId) { - $foreignKeyName = $this->relationModel->getQualifiedKeyName(); - $hydratedModel = $this->relationObject->where($foreignKeyName, $this->manageId)->first(); - - $config->model = $hydratedModel; - if (!$config->model) { - throw new ApplicationException(Lang::get('backend::lang.model.not_found', [ - 'class' => get_class($config->model), 'id' => $this->manageId - ])); - } - } - - $widget = $this->makeWidget('Backend\Widgets\Form', $config); - return $widget; - } - /** - * Returns the active session key. + * Controller override: Extend the query used for populating the list + * after the default query is processed. + * @param October\Rain\Database\Builder $query + * @param string $field */ - public function relationGetSessionKey($force = false) + public function relationExtendQuery($query, $field) { - if ($this->sessionKey && !$force) { - return $this->sessionKey; - } + } - if (post('_relation_session_key')) { - return $this->sessionKey = post('_relation_session_key'); - } - - if (post('_session_key')) { - return $this->sessionKey = post('_session_key'); - } - - return $this->sessionKey = FormHelper::getSessionKey(); + public function relationExtendRefreshResults($field) + { } // // Helpers // + /** + * Returns the existing record IDs for the relation. + */ + protected function findExistingRelationIds($checkIds = null) + { + $foreignKeyName = $this->relationModel->getQualifiedKeyName(); + + $results = $this->relationObject + ->getBaseQuery() + ->select($foreignKeyName); + + if ($checkIds !== null && is_array($checkIds) && count($checkIds)) { + $results = $results->whereIn($foreignKeyName, $checkIds); + } + + return $results->lists($foreignKeyName); + } /** * Determine the default buttons based on the model relationship type. diff --git a/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js b/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js index b541f593d..581feb4ff 100644 --- a/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js +++ b/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js @@ -5,18 +5,8 @@ var RelationBehavior = function() { - this.clickManageListRecord = function(recordId, relationField, sessionKey) { - var oldPopup = $('#relationManagePopup') - - $.request('onRelationClickManageList', { - data: { - 'record_id': recordId, - '_relation_field': relationField, - '_session_key': sessionKey - } - }) - - oldPopup.popup('hide') + this.toggleListCheckbox = function(el) { + $(el).closest('.control-list').listWidget('toggleChecked', [el]) } this.clickViewListRecord = function(recordId, relationField, sessionKey) { @@ -33,6 +23,20 @@ }) } + this.clickManageListRecord = function(recordId, relationField, sessionKey) { + var oldPopup = $('#relationManagePopup') + + $.request('onRelationClickManageList', { + data: { + 'record_id': recordId, + '_relation_field': relationField, + '_session_key': sessionKey + } + }) + + oldPopup.popup('hide') + } + this.clickManagePivotListRecord = function(foreignId, relationField, sessionKey) { var oldPopup = $('#relationManagePivotPopup'), newPopup = $('') diff --git a/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm b/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm index 02f0ca616..6572dc1be 100644 --- a/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm +++ b/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm @@ -17,6 +17,18 @@ + + +
+ + +
+

Recommended

+
+
+
+
+ +
+ + + + + + diff --git a/modules/system/controllers/updates/_install_themes.htm b/modules/system/controllers/updates/_install_themes.htm new file mode 100644 index 000000000..9ea744a83 --- /dev/null +++ b/modules/system/controllers/updates/_install_themes.htm @@ -0,0 +1,104 @@ + +
+ + +
+ +
+ +
+ +
+ + +
+ +

+ Installed themes + (0) +

+ + +
+

There are no themes installed from the marketplace.

+
+ +
    + + +
  • +
    + +
    +
    +

    +

    by

    +
    + +
  • + + +
+ + +
+ +
+
+ + +
+

Recommended

+
+
+
+
+ +
+ +
+ +
+ + diff --git a/modules/system/controllers/updates/_list_search.htm b/modules/system/controllers/updates/_list_search.htm deleted file mode 100644 index edddfa2f1..000000000 --- a/modules/system/controllers/updates/_list_search.htm +++ /dev/null @@ -1,24 +0,0 @@ - - -
- - - - - -
- - diff --git a/modules/system/controllers/updates/_list_toolbar.htm b/modules/system/controllers/updates/_list_toolbar.htm index 8639e2672..c680e089d 100644 --- a/modules/system/controllers/updates/_list_toolbar.htm +++ b/modules/system/controllers/updates/_list_toolbar.htm @@ -11,4 +11,9 @@ class="btn btn-default oc-icon-puzzle-piece"> + + Install plugins + \ No newline at end of file diff --git a/modules/system/controllers/updates/_theme_form.htm b/modules/system/controllers/updates/_theme_form.htm new file mode 100644 index 000000000..8729e8974 --- /dev/null +++ b/modules/system/controllers/updates/_theme_form.htm @@ -0,0 +1,47 @@ + 'themeForm']) ?> + + + + + + \ No newline at end of file diff --git a/modules/system/controllers/updates/config_list.yaml b/modules/system/controllers/updates/config_list.yaml index 02b043b32..5bda9f802 100644 --- a/modules/system/controllers/updates/config_list.yaml +++ b/modules/system/controllers/updates/config_list.yaml @@ -9,4 +9,3 @@ noRecordsMessage: backend::lang.list.no_records toolbar: buttons: list_toolbar - search: list_search \ No newline at end of file diff --git a/modules/system/controllers/updates/install.htm b/modules/system/controllers/updates/install.htm new file mode 100644 index 000000000..5043e13da --- /dev/null +++ b/modules/system/controllers/updates/install.htm @@ -0,0 +1,49 @@ + + + + +fatalError): ?> + +
+ +
+
+
+ makePartial('install_plugins') ?> +
+
+
+
+ makePartial('install_themes') ?> +
+
+
+
+ + + +
+

fatalError) ?>

+

+

+
+ + diff --git a/modules/system/lang/en/lang.php b/modules/system/lang/en/lang.php index 440086139..6c5e9e02e 100644 --- a/modules/system/lang/en/lang.php +++ b/modules/system/lang/en/lang.php @@ -52,6 +52,12 @@ return [ 'my_settings' => 'My Settings' ] ], + 'theme' => [ + 'name' => [ + 'label' => 'Theme Name', + 'help' => 'Name the theme by its unique code. For example, RainLab.Vanilla' + ] + ], 'plugin' => [ 'unnamed' => 'Unnamed plugin', 'name' => [ @@ -156,7 +162,9 @@ return [ 'install' => [ 'project_label' => 'Attach to Project', 'plugin_label' => 'Install Plugin', + 'theme_label' => 'Install Theme', 'missing_plugin_name' => 'Please specify a Plugin name to install.', + 'missing_theme_name' => 'Please specify a Theme name to install.', 'install_completing' => 'Finishing installation process', 'install_success' => 'The plugin has been installed successfully.' ], diff --git a/modules/system/models/pluginversion/columns.yaml b/modules/system/models/pluginversion/columns.yaml index 5bd7b41e5..8d49f9085 100644 --- a/modules/system/models/pluginversion/columns.yaml +++ b/modules/system/models/pluginversion/columns.yaml @@ -4,20 +4,20 @@ columns: - name: - label: system::lang.updates.plugin_name - sortable: false + name: + label: system::lang.updates.plugin_name + sortable: false - description: - label: system::lang.updates.plugin_description - sortable: false + description: + label: system::lang.updates.plugin_description + sortable: false - version: - label: system::lang.updates.plugin_version - sortable: false + version: + label: system::lang.updates.plugin_version + sortable: false - author: - label: system::lang.updates.plugin_author - sortable: false - type: partial - path: column_author \ No newline at end of file + author: + label: system::lang.updates.plugin_author + sortable: false + type: partial + path: column_author \ No newline at end of file From a921201d6d1f71484c992040e85e29c6abfa4110 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sat, 4 Apr 2015 17:31:39 +1100 Subject: [PATCH 27/27] Fixes broken search --- modules/system/controllers/Updates.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/system/controllers/Updates.php b/modules/system/controllers/Updates.php index 2f8646b0e..21c7889ff 100644 --- a/modules/system/controllers/Updates.php +++ b/modules/system/controllers/Updates.php @@ -643,7 +643,7 @@ class Updates extends Controller // Product install // - public function install_onSearchProducts() + public function onSearchProducts() { $searchType = get('search', 'plugins'); $serverUri = $searchType == 'plugins' ? 'plugin/search' : 'theme/search'; @@ -652,7 +652,7 @@ class Updates extends Controller return $manager->requestServerData($serverUri, ['query' => get('query')]); } - public function install_onGetPopularPlugins() + public function onGetPopularPlugins() { $installed = $this->getInstalledPlugins(); $popular = UpdateManager::instance()->requestPopularProducts('plugin'); @@ -661,7 +661,7 @@ class Updates extends Controller return ['result' => $popular]; } - public function install_onGetPopularThemes() + public function onGetPopularThemes() { $installed = $this->getInstalledThemes(); $popular = UpdateManager::instance()->requestPopularProducts('theme');