/** * Guess the HREF for a button. * * @param TableBuilder $builder */ public function guess(TableBuilder $builder) { $buttons = $builder->getButtons(); if (!($section = $this->sections->active())) { return; } if (!($module = $this->modules->active())) { return; } $stream = $builder->getTableStream(); foreach ($buttons as &$button) { // If we already have an HREF then skip it. if (isset($button['attributes']['href'])) { continue; } switch (array_get($button, 'button')) { case 'restore': $button['attributes']['href'] = $this->url->to('entry/handle/restore/' . $module->getNamespace() . '/' . $stream->getNamespace() . '/' . $stream->getSlug() . '/{entry.id}'); break; default: // Determine the HREF based on the button type. $type = array_get($button, 'segment', array_get($button, 'button')); if ($type && !str_contains($type, '\\') && !class_exists($type)) { $button['attributes']['href'] = $section->getHref($type . '/{entry.id}'); } break; } } $builder->setButtons($buttons); }
/** * Handle the command. */ public function handle() { $stream = $this->builder->getTableStream(); if (!$stream instanceof StreamInterface) { return; } $eager = []; if ($stream->isTranslatable()) { $eager[] = 'translations'; } $assignments = $stream->getRelationshipAssignments(); foreach ($this->builder->getColumns() as $column) { /** * If the column value is a string and uses a dot * format then check if it's a relation. */ if (isset($column['value']) && is_string($column['value']) && preg_match("/^entry.([a-zA-Z\\_]+)./", $column['value'], $match)) { if ($assignment = $assignments->findByFieldSlug($match[1])) { if ($assignment->getFieldType()->getNamespace() == 'anomaly.field_type.polymorphic') { continue; } $eager[] = camel_case($match[1]); } } } $this->builder->setTableOption('eager', array_unique($this->builder->getTableOption('eager', []) + $eager)); }
/** * Guess the text for a button. * * @param TableBuilder $builder */ public function guess(TableBuilder $builder) { $buttons = $builder->getButtons(); if (!($module = $this->modules->active())) { return; } if (!($stream = $builder->getTableStream())) { return; } foreach ($buttons as &$button) { if (isset($button['permission'])) { continue; } /** * Try and guess the permission value. */ switch (array_get($button, 'button')) { case 'edit': $button['permission'] = $module->getNamespace($stream->getSlug() . '.write'); break; default: break; } } $builder->setButtons($buttons); }
/** * Set defaults. * * @param TableBuilder $builder */ public function defaults(TableBuilder $builder) { $stream = $builder->getTableStream(); if ($builder->getColumns() == []) { $builder->setColumns([$stream->getTitleColumn()]); } }
/** * Guess the action text. * * @param TableBuilder $builder */ public function guess(TableBuilder $builder) { $actions = $builder->getActions(); $stream = $builder->getTableStream(); if (!($module = $this->modules->active())) { return; } $section = $this->controlPanel->getControlPanelActiveSection(); foreach ($actions as &$action) { /* * Nothing to do if set already. */ if (isset($action['permission'])) { continue; } /* * Try and guess the permission. */ if ($stream) { $action['permission'] = $module->getNamespace($stream->getSlug() . '.' . $action['slug']); } elseif ($section) { $action['permission'] = $module->getNamespace($section->getSlug() . '.' . $action['slug']); } } $builder->setActions($actions); }
/** * Default table views. * * @param TableBuilder $builder */ public function defaults(TableBuilder $builder) { if (!($stream = $builder->getTableStream())) { return; } if ($stream->isTrashable() && !$builder->getViews()) { $builder->setViews(['all', 'trash']); } }
/** * Normalize filter input. * * @param TableBuilder $builder */ public function normalize(TableBuilder $builder) { $filters = $builder->getFilters(); $stream = $builder->getTableStream(); foreach ($filters as $slug => &$filter) { /** * If the filter is a string then use * it for everything. */ if (is_string($filter) && !str_contains($filter, '/')) { $filter = ['slug' => $filter, 'field' => $filter, 'filter' => 'field']; } /** * If the filter is a class string then use * it for the filter. */ if (is_string($filter) && str_contains($filter, '/')) { $filter = ['slug' => $slug, 'filter' => $filter]; } /** * Move the slug into the filter. */ if (!isset($filter['slug'])) { $filter['slug'] = $slug; } /** * Move the slug to the filter. */ if (!isset($filter['filter'])) { $filter['filter'] = $filter['slug']; } /** * Fallback the field. */ if (!isset($filter['field']) && $stream && $stream->hasAssignment($filter['slug'])) { $filter['field'] = $filter['slug']; } /** * If there is no filter type * then assume it's the slug. */ if (!isset($filter['filter'])) { $filter['filter'] = $filter['slug']; } /** * Set the table's stream. */ if ($stream) { $filter['stream'] = $stream; } } $builder->setFilters($filters); }
/** * Handle the command. * * @param ModuleCollection $modules */ public function handle(ModuleCollection $modules) { $table = $this->builder->getTable(); /** * Set the default sortable option. */ if ($table->getOption('sortable') === null) { $stream = $table->getStream(); if ($stream && $stream->isSortable()) { $table->setOption('sortable', true); } } /** * Sortable tables have no pages. */ if ($table->getOption('sortable') === true) { $table->setOption('limit', $table->getOption('limit', 99999)); } /** * Set the default breadcrumb. */ if ($table->getOption('breadcrumb') === null && ($title = $table->getOption('title'))) { $table->setOption('breadcrumb', $title); } /** * If the table ordering is currently being overridden * then set the values from the request on the builder * last so it actually has an effect. */ if ($orderBy = $this->builder->getRequestValue('order_by')) { $table->setOption('order_by', [$orderBy => $this->builder->getRequestValue('sort', 'asc')]); } /** * If the permission is not set then * try and automate it. */ if ($table->getOption('permission') === null && ($module = $modules->active()) && ($stream = $this->builder->getTableStream())) { $table->setOption('permission', $module->getNamespace($stream->getSlug() . '.read')); } }
/** * Guess some table table filter placeholders. * * @param TableBuilder $builder */ public function guess(TableBuilder $builder) { $filters = $builder->getFilters(); $stream = $builder->getTableStream(); foreach ($filters as &$filter) { // Skip if we already have a placeholder. if (isset($filter['placeholder'])) { continue; } // Get the placeholder off the assignment. if ($stream && ($assignment = $stream->getAssignment(array_get($filter, 'field')))) { /* * Always use the field name * as the placeholder. Placeholders * that are assigned otherwise usually * feel out of context: * * "Choose an option..." in the filter * would just be weird. */ $placeholder = $assignment->getFieldName(); if ($this->translator->has($placeholder)) { $filter['placeholder'] = $placeholder; } } if (!($module = $this->modules->active())) { continue; } $placeholder = $module->getNamespace('field.' . $filter['slug'] . '.placeholder'); if (!isset($filter['placeholder']) && $this->translator->has($placeholder)) { $filter['placeholder'] = $placeholder; } $placeholder = $module->getNamespace('field.' . $filter['slug'] . '.name'); if (!isset($filter['placeholder']) && $this->translator->has($placeholder)) { $filter['placeholder'] = $placeholder; } if (!array_get($filter, 'placeholder')) { $filter['placeholder'] = $filter['slug']; } if (!$this->translator->has($filter['placeholder']) && $this->config->get('streams::system.lazy_translations')) { $filter['placeholder'] = ucwords($this->string->humanize($filter['placeholder'])); } } $builder->setFilters($filters); }
/** * ExportAll the selected entries. * * @param TableBuilder $builder * @param ResponseFactory $response * @param array $selected */ public function handle(TableBuilder $builder, ResponseFactory $response, array $selected) { $model = $builder->getTableModel(); $stream = $builder->getTableStream(); $headers = ['Content-Disposition' => 'attachment; filename=' . $stream->getSlug() . '.csv', 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', 'Content-type' => 'text/csv', 'Pragma' => 'public', 'Expires' => '0']; $callback = function () use($selected, $model) { $output = fopen('php://output', 'w'); /* @var EloquentModel $entry */ foreach ($model->all() as $k => $entry) { if ($k == 0) { fputcsv($output, array_keys($entry->toArray())); } fputcsv($output, $entry->toArray()); } fclose($output); }; $builder->setTableResponse($response->stream($callback, 200, $headers)); }
/** * Guess the sortable flags for headers. * * @param TableBuilder $builder */ public function guess(TableBuilder $builder) { $columns = $builder->getColumns(); $stream = $builder->getTableStream(); foreach ($columns as &$column) { if ($builder->getTableOption('sortable_headers') === false) { $column['sortable'] = false; continue; } /* * If the heading is false or does not exist * then the intent was to not have * heading text at all. */ if (!isset($column['heading']) || $column['heading'] === false) { continue; } /* * If sortable is already set the we don't * need to guess anything. */ if (isset($column['sortable'])) { continue; } /* * If the sort column is set and * sortable is not yet, set it. */ if (isset($column['sort_column'])) { $column['sortable'] = true; continue; } /* * No stream means we can't * really do much here. */ if (!$stream instanceof StreamInterface) { continue; } /* * We're going to be using the value to * try and determine if a streams field is * being used. No value, no guess. */ if (!isset($column['value']) || !$column['value'] || !is_string($column['value'])) { continue; } /* * Now we're going to try and determine * what streams field this column if * using if any at all. */ $field = $column['value']; /* * If the value matches a field * with dot format then reduce it. */ if (preg_match("/^entry.([a-zA-Z\\_]+)/", $column['value'], $match)) { $field = $match[1]; } /* * If we can't determine a field type * then we don't have anything to base * our guess off of. */ if (!($assignment = $stream->getAssignment($field))) { continue; } $type = $assignment->getFieldType(); /* * If the field type has a database * column type then we can sort on it * by default! * * @todo: Allow sorting of translatable fields. */ if ($type->getColumnType() && !$assignment->isTranslatable()) { $column['sortable'] = true; $column['sort_column'] = $type->getColumnName(); } else { $column['sortable'] = false; } } $builder->setColumns($columns); }
/** * Guess the field for a column. * * @param TableBuilder $builder */ public function guess(TableBuilder $builder) { $columns = $builder->getColumns(); $stream = $builder->getTableStream(); $module = $this->modules->active(); foreach ($columns as &$column) { /* * If the heading is already set then * we don't have anything to do. */ if (isset($column['heading'])) { continue; } /* * If the heading is false, then no * header is desired at all. */ if (isset($column['heading']) && $column['heading'] === false) { continue; } /* * No stream means we can't * really do much here. */ if (!$stream instanceof StreamInterface) { continue; } if (!isset($column['field']) && is_string($column['value'])) { $column['field'] = $column['value']; } /* * If the heading matches a field * with dot format then reduce it. */ if (isset($column['field']) && preg_match("/^entry.([a-zA-Z\\_]+)/", $column['field'], $match)) { $column['field'] = $match[1]; } /* * Detect some built in columns. */ if (in_array($column['field'], ['id', 'created_at', 'created_by', 'updated_at', 'updated_by'])) { $column['heading'] = 'streams::entry.' . $column['field']; continue; } /* * Detect entry title. */ if (in_array($column['field'], ['view_link', 'edit_link']) && ($field = $stream->getTitleField())) { $column['heading'] = $field->getName(); continue; } $field = $stream->getField(array_get($column, 'field')); /* * Detect the title column. */ $title = $stream->getTitleField(); if ($title && !$field && $column['field'] == 'title' && $this->translator->has($heading = $title->getName())) { $column['heading'] = $heading; } /* * Use the name from the field. */ if ($field && ($heading = $field->getName())) { $column['heading'] = $heading; } /* * If no field look for * a name anyways. */ if ($module && !$field && $this->translator->has($heading = $module->getNamespace('field.' . $column['field'] . '.name'))) { $column['heading'] = $heading; } /* * If no translatable heading yet and * the heading matches the value (default) * then humanize the heading value. */ if (!isset($column['heading']) && $this->config->get('streams::system.lazy_translations')) { $column['heading'] = ucwords($this->string->humanize($column['field'])); } /* * If we have a translatable heading and * the heading does not have a translation * then humanize the heading value. */ if (isset($column['heading']) && str_is('*.*.*::*', $column['heading']) && !$this->translator->has($column['heading']) && $this->config->get('streams::system.lazy_translations')) { $column['heading'] = ucwords($this->string->humanize($column['field'])); } /* * Last resort. */ if ($module && !isset($column['heading'])) { $column['heading'] = $module->getNamespace('field.' . $column['field'] . '.name'); } } $builder->setColumns($columns); }
/** * Handle the filter query. * * @param Builder $query * @param FilterInterface $filter * @param TableBuilder $builder */ public function filter(Builder $query, FilterInterface $filter, TableBuilder $builder) { $stream = $builder->getTableStream(); $table = $stream->getEntryTableName() . '_' . $filter->getField(); $query->join($table . ' AS filter_' . $filter->getField(), $stream->getEntryTableName() . '.id', '=', 'filter_' . $filter->getField() . '.entry_id')->where('filter_' . $filter->getField() . '.entry_id', $filter->getValue()); }
/** * Get the table entries. * * @param TableBuilder $builder * @return Collection */ public function get(TableBuilder $builder) { // Grab any stream we have. $stream = $builder->getTableStream(); // Start a new query. $query = $this->model->newQuery(); /* * Prevent joins from overriding intended columns * by prefixing with the model's table name. */ $query = $query->select($this->model->getTable() . '.*'); /* * Eager load any relations to * save resources and queries. */ $query = $query->with($builder->getTableOption('eager', [])); /* * Raise and fire an event here to allow * other things (including filters / views) * to modify the query before proceeding. */ $builder->fire('querying', compact('builder', 'query')); app('events')->fire(new TableIsQuerying($builder, $query)); /* * Before we actually adjust the baseline query * set the total amount of entries possible back * on the table so it can be used later. */ $total = $query->count(); $builder->setTableOption('total_results', $total); /* * Assure that our page exists. If the page does * not exist then start walking backwards until * we find a page that is has something to show us. */ $limit = (int) $builder->getTableOption('limit', config('streams::system.per_page', 15)); $page = app('request')->get($builder->getTableOption('prefix') . 'page', 1); $offset = $limit * ($page - 1); if ($total < $offset && $page > 1) { $url = str_replace($builder->getTableOption('prefix') . 'page=' . $page, $builder->getTableOption('prefix') . 'page=' . ($page - 1), app('request')->fullUrl()); header('Location: ' . $url); } /* * Limit the results to the limit and offset * based on the page if any. */ $offset = $limit * (app('request')->get($builder->getTableOption('prefix') . 'page', 1) - 1); $query = $query->take($limit)->offset($offset); /* * Order the query results. */ if ($order = $builder->getTableOption('order_by')) { foreach ($order as $column => $direction) { if ($stream && ($utility = $stream->getFieldTypeQuery($column))) { $utility->orderBy($query, $direction); } else { $query = $query->orderBy($column, $direction); } } } if ($builder->getTableOption('sortable')) { $query = $query->orderBy('sort_order', 'ASC'); } return $query->get(); }