listConfig)) { $this->listDefinitions = $controller->listConfig; $this->primaryDefinition = key($this->listDefinitions); } else { $this->listDefinitions = ['list' => $controller->listConfig]; $this->primaryDefinition = 'list'; } /* * Build configuration */ $this->setConfig($this->listDefinitions[$this->primaryDefinition], $this->requiredConfig); } /** * Creates all the list widgets based on the definitions. * @return array */ public function makeLists() { foreach ($this->listDefinitions as $definition => $config) { $this->listWidgets[$definition] = $this->makeList($definition); } return $this->listWidgets; } /** * Prepare the widgets used by this action * @return \Backend\Widgets\Lists */ public function makeList($definition = null) { if (!$definition || !isset($this->listDefinitions[$definition])) { $definition = $this->primaryDefinition; } $listConfig = $this->controller->listGetConfig($definition); /* * Create the model */ $class = $listConfig->modelClass; $model = new $class; $model = $this->controller->listExtendModel($model, $definition); /* * Prepare the list widget */ $columnConfig = $this->makeConfig($listConfig->list); $columnConfig->model = $model; $columnConfig->alias = $definition; /* * Prepare the columns configuration */ $configFieldsToTransfer = [ 'recordUrl', 'recordOnClick', 'recordsPerPage', 'showPageNumbers', 'noRecordsMessage', 'defaultSort', 'showSorting', 'showSetup', 'showCheckboxes', 'showTree', 'treeExpanded', 'customViewPath', ]; foreach ($configFieldsToTransfer as $field) { if (isset($listConfig->{$field})) { $columnConfig->{$field} = $listConfig->{$field}; } } /* * List Widget with extensibility */ $widget = $this->makeWidget(\Backend\Widgets\Lists::class, $columnConfig); $widget->bindEvent('list.extendColumns', function () use ($widget) { $this->controller->listExtendColumns($widget); }); $widget->bindEvent('list.extendQueryBefore', function ($query) use ($definition) { $this->controller->listExtendQueryBefore($query, $definition); }); $widget->bindEvent('list.extendQuery', function ($query) use ($definition) { $this->controller->listExtendQuery($query, $definition); }); $widget->bindEvent('list.extendRecords', function ($records) use ($definition) { return $this->controller->listExtendRecords($records, $definition); }); $widget->bindEvent('list.injectRowClass', function ($record) use ($definition) { return $this->controller->listInjectRowClass($record, $definition); }); $widget->bindEvent('list.overrideColumnValue', function ($record, $column, $value) use ($definition) { return $this->controller->listOverrideColumnValue($record, $column->columnName, $definition); }); $widget->bindEvent('list.overrideHeaderValue', function ($column, $value) use ($definition) { return $this->controller->listOverrideHeaderValue($column->columnName, $definition); }); $widget->bindToController(); /* * Prepare the toolbar widget (optional) */ if (isset($listConfig->toolbar)) { $toolbarConfig = $this->makeConfig($listConfig->toolbar); $toolbarConfig->alias = $widget->alias . 'Toolbar'; $toolbarWidget = $this->makeWidget(\Backend\Widgets\Toolbar::class, $toolbarConfig); $toolbarWidget->bindToController(); $toolbarWidget->cssClasses[] = 'list-header'; /* * Link the Search Widget to the List Widget */ if ($searchWidget = $toolbarWidget->getSearchWidget()) { $searchWidget->bindEvent('search.submit', function () use ($widget, $searchWidget) { $widget->setSearchTerm($searchWidget->getActiveTerm(), true); return $widget->onRefresh(); }); $widget->setSearchOptions([ 'mode' => $searchWidget->mode, 'scope' => $searchWidget->scope, ]); // Find predefined search term $widget->setSearchTerm($searchWidget->getActiveTerm()); } $this->toolbarWidgets[$definition] = $toolbarWidget; } /* * Prepare the filter widget (optional) */ if (isset($listConfig->filter)) { $widget->cssClasses[] = 'list-flush'; $filterConfig = $this->makeConfig($listConfig->filter); $filterConfig->alias = $widget->alias . 'Filter'; $filterWidget = $this->makeWidget(\Backend\Widgets\Filter::class, $filterConfig); $filterWidget->bindToController(); /* * Filter the list when the scopes are changed */ $filterWidget->bindEvent('filter.update', function () use ($widget, $filterWidget) { return $widget->onFilter(); }); /* * Filter Widget with extensibility */ $filterWidget->bindEvent('filter.extendScopes', function () use ($filterWidget) { $this->controller->listFilterExtendScopes($filterWidget); }); /* * Extend the query of the list of options */ $filterWidget->bindEvent('filter.extendQuery', function ($query, $scope) { $this->controller->listFilterExtendQuery($query, $scope); }); // Apply predefined filter values $widget->addFilter([$filterWidget, 'applyAllScopesToQuery']); $this->filterWidgets[$definition] = $filterWidget; } return $widget; } /** * Index Controller action. * @return void */ public function index() { $this->controller->pageTitle = $this->controller->pageTitle ?: Lang::get($this->getConfig( 'title', 'backend::lang.list.default_title' )); $this->controller->bodyClass = 'slim-container'; $this->makeLists(); } /** * Bulk delete records. * @return void * @throws \October\Rain\Exception\ApplicationException when the parent definition is missing. */ public function index_onDelete() { if (method_exists($this->controller, 'onDelete')) { return call_user_func_array([$this->controller, 'onDelete'], func_get_args()); } /* * Validate checked identifiers */ $checkedIds = post('checked'); if (!$checkedIds || !is_array($checkedIds) || !count($checkedIds)) { Flash::error(Lang::get('backend::lang.list.delete_selected_empty')); return $this->controller->listRefresh(); } /* * Establish the list definition */ $definition = post('definition', $this->primaryDefinition); if (!isset($this->listDefinitions[$definition])) { throw new ApplicationException(Lang::get('backend::lang.list.missing_parent_definition', compact('definition'))); } $listConfig = $this->controller->listGetConfig($definition); /* * Create the model */ $class = $listConfig->modelClass; $model = new $class; $model = $this->controller->listExtendModel($model, $definition); /* * Create the query */ $query = $model->newQuery(); $this->controller->listExtendQueryBefore($query, $definition); $query->whereIn($model->getKeyName(), $checkedIds); $this->controller->listExtendQuery($query, $definition); /* * Delete records */ $records = $query->get(); if ($records->count()) { foreach ($records as $record) { $record->delete(); } Flash::success(Lang::get('backend::lang.list.delete_selected_success')); } else { Flash::error(Lang::get('backend::lang.list.delete_selected_empty')); } return $this->controller->listRefresh($definition); } /** * Renders the widget collection. * @param string $definition Optional list definition. * @return string Rendered HTML for the list. * @throws \October\Rain\Exception\ApplicationException when there are no list widgets set. */ public function listRender($definition = null) { if (!count($this->listWidgets)) { throw new ApplicationException(Lang::get('backend::lang.list.behavior_not_ready')); } if (!$definition || !isset($this->listDefinitions[$definition])) { $definition = $this->primaryDefinition; } $listConfig = $this->controller->listGetConfig($definition); $vars = [ 'toolbar' => null, 'filter' => null, 'list' => null, ]; if (isset($this->toolbarWidgets[$definition])) { $vars['toolbar'] = $this->toolbarWidgets[$definition]; } if (isset($this->filterWidgets[$definition])) { $vars['filter'] = $this->filterWidgets[$definition]; } $vars['list'] = $this->listWidgets[$definition]; return $this->listMakePartial('container', $vars); } /** * Controller accessor for making partials within this behavior. * @param string $partial * @param array $params * @return string Partial contents */ public function listMakePartial($partial, $params = []) { $contents = $this->controller->makePartial('list_'.$partial, $params + $this->vars, false); if (!$contents) { $contents = $this->makePartial($partial, $params); } return $contents; } /** * Refreshes the list container only, useful for returning in custom AJAX requests. * @param string $definition Optional list definition. * @return array The list element selector as the key, and the list contents are the value. */ public function listRefresh($definition = null) { if (!count($this->listWidgets)) { $this->makeLists(); } if (!$definition || !isset($this->listDefinitions[$definition])) { $definition = $this->primaryDefinition; } return $this->listWidgets[$definition]->onRefresh(); } /** * Returns the widget used by this behavior. * @return \Backend\Classes\WidgetBase */ public function listGetWidget($definition = null) { if (!$definition) { $definition = $this->primaryDefinition; } return array_get($this->listWidgets, $definition); } /** * Returns the configuration used by this behavior. * @return \Backend\Classes\WidgetBase */ public function listGetConfig($definition = null) { if (!$definition) { $definition = $this->primaryDefinition; } if (!$config = array_get($this->listConfig, $definition)) { $config = $this->listConfig[$definition] = $this->makeConfig($this->listDefinitions[$definition], $this->requiredConfig); } return $config; } // // Overrides // /** * Called after the list columns are defined. * @param \Backend\Widgets\Lists $host The hosting list widget * @return void */ public function listExtendColumns($host) { } /** * Called after the filter scopes are defined. * @param \Backend\Widgets\Filter $host The hosting filter widget * @return void */ public function listFilterExtendScopes($host) { } /** * Controller override: Extend supplied model * @param \October\Rain\Database\Model $model * @param string|null $definition * @return \October\Rain\Database\Model */ public function listExtendModel($model, $definition = null) { return $model; } /** * Controller override: Extend the query used for populating the list * before the default query is processed. * @param \October\Rain\Database\Builder $query * @param string|null $definition */ public function listExtendQueryBefore($query, $definition = null) { } /** * Controller override: Extend the query used for populating the list * after the default query is processed. * @param \October\Rain\Database\Builder $query * @param string|null $definition */ public function listExtendQuery($query, $definition = null) { } /** * Controller override: Extend the records used for populating the list * after the query is processed. * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection $records * @param string|null $definition */ public function listExtendRecords($records, $definition = null) { } /** * Controller override: Extend the query used for populating the filter * options before the default query is processed. * @param \October\Rain\Database\Builder $query * @param array $scope */ public function listFilterExtendQuery($query, $scope) { } /** * Returns a CSS class name for a list row (). * @param \October\Rain\Database\Model $record The populated model used for the column * @param string|null $definition List definition (optional) * @return string|void CSS class name */ public function listInjectRowClass($record, $definition = null) { } /** * Replace a table column value (...) * @param \October\Rain\Database\Model $record The populated model used for the column * @param string $columnName The column name to override * @param string|null $definition List definition (optional) * @return string|void HTML view */ public function listOverrideColumnValue($record, $columnName, $definition = null) { } /** * Replace the entire table header contents (...) with custom HTML * @param string $columnName The column name to override * @param string|null $definition List definition (optional) * @return string|void HTML view */ public function listOverrideHeaderValue($columnName, $definition = null) { } /** * Static helper for extending list columns. * @param callable $callback * @return void */ public static function extendListColumns($callback) { $calledClass = self::getCalledExtensionClass(); Event::listen('backend.list.extendColumns', function (\Backend\Widgets\Lists $widget) use ($calledClass, $callback) { if (!is_a($widget->getController(), $calledClass)) { return; } call_user_func_array($callback, [$widget, $widget->model]); }); } /** * Static helper for extending filter scopes. * @param callable $callback * @return void */ public static function extendListFilterScopes($callback) { $calledClass = self::getCalledExtensionClass(); Event::listen('backend.filter.extendScopes', function (\Backend\Widgets\Filter $widget) use ($calledClass, $callback) { if (!is_a($widget->getController(), $calledClass)) { return; } call_user_func_array($callback, [$widget]); }); } }