/** * Filter the supplied Fields object using the filter's callback. * * @param Fields $fields * @return Fields */ public function apply(Fields $fields) { $filteredFields = new Fields([], $fields->getUser()); foreach ($fields as $field) { if (true === call_user_func($this->callback, $field)) { $filteredFields->add($field); } } return $filteredFields; }
private function getFields() { $fields = new Fields(); $fields->add('one')->setEditable(true)->setLabel('One')->assignHelperCallback('EditControl.Control', function ($helper, $view) { return 'FIELD_ONE'; })->add('two')->setEditable(true)->setLabel('Two')->assignHelperCallback('EditControl.Control', function ($helper, $view) { return 'FIELD_TWO'; })->add('three')->setEditable(false)->setLabel('Not Editable'); return $fields; }
/** * When calling add() on this field, it will delegate the call back up to * the associated \Dewdrop\Fields object. This allows for a very fluid * method chaining style when defining a large set of fields. * * @param mixed $field * @param string $modelName * @throws Exception * @return mixed */ public function add($field, $modelName = null) { if (!$this->fieldsSet) { throw new Exception('Cannot add field because no \\Dewdrop\\Fields object is available'); } return $this->fieldsSet->add($field, $modelName); }
public function testCanSetUserObject() { require_once __DIR__ . '/fields-test/TestUser.php'; $user = new \DewdropTest\TestUser(); $this->fields->setUser($user); $this->assertInstanceOf('Dewdrop\\Fields\\UserInterface', $this->fields->getUser()); }
public function testCanOptionallySupplyCustomRenderer() { $fields = new Fields(); $fields->add('custom')->setVisible(true)->setLabel('Custom')->assignHelperCallback('CsvCell.Content', function ($helper, $rowData) { return 'shouldnotberendered'; })->add('not_visible')->setVisible(false)->setLabel('Unseen')->assignHelperCallback('CsvCell.Content', function ($helper, $rowData) { return 'content.unseen'; }); $renderer = $this->view->csvCellRenderer(); $renderer->getContentRenderer()->assign('custom', function ($helper, $rowData) { return 'customrendering'; }); $output = $this->csvExportViewHelper->direct($fields, array(array('test_field' => 'FAFAFAFA')), $renderer); $this->assertContains('customrendering', $output); $this->assertNotContains('shouldnotberenderered', $output); }
/** * Remove a field from this group and the GroupedFields container. * * @param string $id * @return Group */ public function remove($id) { // This check ensures we don't enter an infinite loop because the set will try to remove from the group as well if ($this->groupedFields->has($id)) { $this->groupedFields->remove($id); } return parent::remove($id); }
/** * Create any resources that need to be accessible both for processing * and rendering. * * @return void */ public function init() { $this->model = Pimple::getResource('users-gateway'); $this->fields = new Fields(); $this->row = $this->model->find($this->request->getQuery('user_id')); $this->fields->add('password')->setLabel('Password')->setEditable(true)->add('confirm_password')->setLabel('Confirm Password')->setEditable(true); $this->inputFilter = new InputFilter(); $password = new Input('password'); $this->inputFilter->add($password); $password->setRequired(true)->getValidatorChain()->attach(new StringLength(array('min' => 6))); $confirm = new Input('confirm_password'); $this->inputFilter->add($confirm); $validator = new Callback(array('callback' => function ($value) { return $value === $this->request->getPost('password'); })); $validator->setMessage('Passwords do not match.'); $confirm->setRequired(true)->getValidatorChain()->attach($validator); }
/** * Remove a field from this collection. This will remove it from the * core collection and whatever group it is a member of. * * @param string $id * @return GroupedFields */ public function remove($id) { parent::remove($id); foreach ($this->groups as $group) { if ($group->has($id)) { $group->remove($id); } } return $this; }
private function buildFields() { $fields = new Fields(); $fields->add('file')->setLabel('File')->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCell $helper, array $rowData) { if (!isset($rowData['file'])) { return null; } return $helper->getView()->escapeHtml($rowData['file']); })->add('line')->setLabel('Line')->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCell $helper, array $rowData) { if (!isset($rowData['line'])) { return null; } return $helper->getView()->escapeHtml($rowData['line']); })->add('function')->setLabel('Function')->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCell $helper, array $rowData) { $out = ''; if (!isset($rowData['class'])) { $out .= $helper->getView()->escapeHtml($rowData['function']); } else { $out .= $helper->getView()->escapeHtml("{$rowData['class']}{$rowData['type']}{$rowData['function']}"); } $out .= '('; $argStrings = []; foreach ($rowData['args'] as $arg) { if (is_array($arg)) { $argStrings[] = 'Array'; } elseif (is_object($arg)) { $argStrings[] = get_class($arg); } else { $argStrings[] = var_export($arg, true); } } $out .= implode(', ', $argStrings); $out .= ')'; return $out; }); return $fields; }
/** * Build a Fields object that can be used for displaying or editing * subscriptions. * * @todo Refactor this into a separate class. * * @param string $editUrl * @param Fields $componentFields * @return Fields */ public function buildFields($editUrl, Fields $componentFields) { $fields = new Fields(); $fields->add('recipients')->setLabel('Recipients')->setNote('Enter one or more email addresses separated by commas.')->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCellHelper $helper, array $rowData) use($editUrl) { return $helper->getView()->escapeHtml($this->renderRecipients($rowData['dewdrop_notification_subscription_id'])); })->setEditable(true)->assignHelperCallback('EditControl.Control', function ($helper, View $view) { return $view->inputText('recipients', $this->renderRecipients($view->getRequest()->getQuery('dewdrop_notification_subscription_id')), 'form-control'); })->assignHelperCallback('InputFilter', function ($helper) { $input = new Input('recipients'); $input->setAllowEmpty(false); return $input; })->add($this->field('dewdrop_notification_frequency_id'))->add('fields')->setLabel('Which fields would you like to include in the notification emails?')->setEditable(true)->assignHelperCallback('EditControl.Control', function ($helper, View $view) use($componentFields) { $options = array(); foreach ($componentFields->getVisibleFields() as $id => $field) { $options[$id] = $field->getLabel(); } return $view->checkboxList('fields', $options, $this->getSelectedFields($view->getRequest()->getQuery('dewdrop_notification_subscription_id'), $options)); })->assignHelperCallback('InputFilter', function ($helper) { $input = new Input('fields'); $input->setAllowEmpty(false); return $input; }); return $fields; }
public function testSavingWillTraverseLinkedFieldsToHookRowsTogether() { $db = Pimple::getResource('db'); $db->query('DELETE FROM dewdrop_test_fruits'); $animalModel = new RowEditorAnimalModel(); $this->fields->add($animalModel->field('name')); $this->fields->add($animalModel->field('is_fierce')); $this->fields->add($animalModel->field('is_cute')); $this->rowEditor->linkByQueryString('dewdrop_test_animals', 'dewdrop_test_animal_id'); $this->rowEditor->linkByField('dewdrop_test_fruits', $animalModel->field('favorite_fruit_id')); $this->rowEditor->link(); $this->assertTrue($this->rowEditor->isValid(array('dewdrop_test_fruits:name' => 'Banana', 'dewdrop_test_fruits:level_of_deliciousness' => 8, 'dewdrop_test_animals:name' => 'Gorilla', 'dewdrop_test_animals:is_fierce' => 1, 'dewdrop_test_animals:is_cute' => 1))); $this->rowEditor->save(); $this->assertEquals($db->fetchOne('SELECT MAX(dewdrop_test_fruit_id) FROM dewdrop_test_fruits'), $db->fetchOne('SELECT favorite_fruit_id FROM dewdrop_test_animals ORDER BY dewdrop_test_animal_id DESC LIMIT 1')); }
public function testCanInsertAFieldIntoAPositionImmediatelyAfterAnExistingField() { // Insert a field after the first field $this->assertInstanceOf('Dewdrop\\Fields\\FieldInterface', $this->fields->insertAfter('second', 'visible')); $this->assertCount(5, $this->fields); $this->assertTrue($this->fields->has('visible', $visiblePosition)); $this->assertSame(0, $visiblePosition); $this->assertTrue($this->fields->has('second', $secondPosition)); $this->assertSame(1, $secondPosition); $this->assertTrue($this->fields->has('sortable', $sortablePosition)); $this->assertSame(2, $sortablePosition); $this->assertTrue($this->fields->has('editable', $editablePosition)); $this->assertSame(3, $editablePosition); $this->assertTrue($this->fields->has('filterable', $filterablePosition)); $this->assertSame(4, $filterablePosition); // Insert a field after some field in the middle $this->assertInstanceOf('Dewdrop\\Fields\\FieldInterface', $this->fields->insertAfter('fourth', 'sortable')); $this->assertCount(6, $this->fields); $this->assertTrue($this->fields->has('visible', $visiblePosition)); $this->assertSame(0, $visiblePosition); $this->assertTrue($this->fields->has('second', $secondPosition)); $this->assertSame(1, $secondPosition); $this->assertTrue($this->fields->has('sortable', $sortablePosition)); $this->assertSame(2, $sortablePosition); $this->assertTrue($this->fields->has('fourth', $sortablePosition)); $this->assertSame(3, $sortablePosition); $this->assertTrue($this->fields->has('editable', $editablePosition)); $this->assertSame(4, $editablePosition); $this->assertTrue($this->fields->has('filterable', $filterablePosition)); $this->assertSame(5, $filterablePosition); // Insert a field after the last field $this->assertInstanceOf('Dewdrop\\Fields\\FieldInterface', $this->fields->insertAfter('last', 'filterable')); $this->assertCount(7, $this->fields); $this->assertTrue($this->fields->has('visible', $visiblePosition)); $this->assertSame(0, $visiblePosition); $this->assertTrue($this->fields->has('second', $secondPosition)); $this->assertSame(1, $secondPosition); $this->assertTrue($this->fields->has('sortable', $sortablePosition)); $this->assertSame(2, $sortablePosition); $this->assertTrue($this->fields->has('fourth', $sortablePosition)); $this->assertSame(3, $sortablePosition); $this->assertTrue($this->fields->has('editable', $editablePosition)); $this->assertSame(4, $editablePosition); $this->assertTrue($this->fields->has('filterable', $filterablePosition)); $this->assertSame(5, $filterablePosition); $this->assertTrue($this->fields->has('last', $filterablePosition)); $this->assertSame(6, $filterablePosition); }
public function renderAjaxResponse() { if (!$this->request->isPost() && !$this->request->isGet()) { return ['result' => 'error', 'message' => 'AJAX edit requests must be POST or GET']; } elseif ($this->request->isPost() && !$this->invalidSubmission) { return ['result' => 'success', 'id' => $this->component->getListing()->getPrimaryKey()->getValue(), 'data' => $this->getData()]; } elseif ($this->request->isGet()) { return $this->renderAjaxForm(); } else { $messages = []; foreach ($this->fields->getEditableFields() as $field) { $messages[$field->getHtmlId()] = $this->rowEditor->getMessages($field); } return ['result' => 'invalid', 'messages' => $messages]; } }
public function renderFieldsInTableRow(Fields $fields, InputFilter $inputFilter, Renderer $renderer) { $output = '<tr>'; foreach ($fields->getEditableFields() as $field) { $output .= '<td>'; $output .= $this->renderFieldContent($field, $inputFilter, $renderer, 100, false); $output .= '</td>'; } $output .= '</tr>'; return $output; }
/** * Build a Fields object that can be used when rendering the grouped counts. Supplies a field * to render the actual HTML value from the original Fields object used when fetching data for * the listing and a field to render the count for that value. We don't do any escaping here * because we assume that the original Fields object handled that in its rendering code. The * fields object returned from this method supports both TableCell and CsvCell rendering. * * @return Fields */ public function buildRenderFields() { $fields = new Fields(); $fields->add('content')->setLabel($this->groupField->getLabel())->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCell\Content $helper, array $rowData) { // Not escaping here because we assume it was escaped by the original renderer in fetchData() return $rowData['content']; })->assignHelperCallback('CsvCell.Content', function (CsvCell\Content $helper, array $rowData) { return $rowData['content']; })->add('count')->setLabel('Count')->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCell\Content $helper, array $rowData) { return $helper->getView()->escapeHtml($rowData['count']); })->assignHelperCallback('CsvCell.Content', function (CsvCell\Content $helper, array $rowData) { return $rowData['count']; }); return $fields; }
/** * Apply the filter to the supplied set of fields. In return, you'll end * up with a \Dewdrop\Fields\GroupedFields object that reflects the sort * order and grouping preferences contained in this filter. You can use * that object either as a normal \Dewdrop\Fields object or you can call * getGroups() on it to get the fields back in their assigned groups. * * @param Fields $currentFields * @return GroupedFields */ public function apply(Fields $currentFields) { if (!$this->loadedDbData) { $this->loadedDbData = $this->load(); } $groupedFields = new GroupedFields([], $currentFields->getUser()); foreach ($this->loadedDbData as $fieldConfig) { // Ungrouped fields come after grouped fields if (!$fieldConfig['group_id']) { continue; } $fieldId = $fieldConfig['field_id']; $groupId = $fieldConfig['group_id']; if ($currentFields->has($fieldId)) { if (!$groupedFields->hasGroup($groupId)) { $group = $groupedFields->addGroup($groupId); $group->setTitle($fieldConfig['group_title']); } $groupedFields->getGroup($groupId)->add($currentFields->get($fieldId)); } } $ungrouped = $groupedFields->addGroup('ungrouped'); $ungrouped->setTitle('Other'); foreach ($this->loadedDbData as $fieldConfig) { if (!$fieldConfig['group_id']) { $id = $fieldConfig['field_id']; if ($currentFields->has($id)) { $ungrouped->add($currentFields->get($id)); } } } foreach ($currentFields as $field) { if (!$groupedFields->has($field->getId())) { $ungrouped->add($field); } } return $groupedFields; }
/** * Provide a Fields object with all the available fields the user can select * from along with a Fields object containing only those that have been * selected to be displayed already. The helper will use these two Fields * objects to render a modal that enables a user to check/un-check columns * they'd like to have displayed. * * @param Fields $available * @param Fields $visible * @param string $actionUrl * @param boolean $filterByUser * @param string $id * @return string */ public function direct(Fields $available, Fields $visible, $actionUrl, $filterByUser = false, $id = null) { return $this->partial('bootstrap-columns-modal.phtml', array('id' => $id ?: 'adjust-columns-modal', 'actionUrl' => $actionUrl, 'visible' => $visible->getVisibleFields(), 'available' => $available->getVisibleFields(), 'filterByUser' => $filterByUser)); }
private function getFields() { $fields = new Fields(); $fields->add($this->model->field('name'))->add($this->model->field('is_delicious')); return $fields; }
/** * Apply the field to the supplied set of fields. If after filtering no * fields are left, we'll return the full set of fields as a fallback. * If no preferences are saved to the DB, we will use either pre-defined * defaults (@see setDefaults()) or the first 4 fields. * * @param Fields $fields * @return Fields */ public function apply(Fields $fields) { if (!$this->selections) { $this->selections = $this->load(); } if (0 !== count($this->defaultFields)) { $defaults = $this->defaultFields; } else { $defaults = []; foreach ($fields as $field) { if (4 > count($defaults)) { $defaults[] = $field->getId(); } } } $output = new Fields([], $fields->getUser()); foreach ($fields as $field) { if (in_array($field->getId(), $this->selections) || !count($this->selections) && in_array($field->getId(), $defaults)) { $output[] = $field; } } // If there are no fields found in the output, return the original fields array as a fallback if (!count($output)) { return $fields; } return $output; }
private function decorateFields(Fields $fields, EditControl $renderer) { $control = $renderer->getControlRenderer(); /* @var $field FieldInterface */ foreach ($fields->getEditableFields() as $field) { $callback = $control->getFieldAssignment($field); $control->assign($field, function () use($callback, $control, $field) { return $control->getView()->importEditControl($field, $this->importFile, $this->request, $callback($control, $control->getView())); }); } return $fields; }
/** * Given the supplied $fields and \Dewdrop\Request object, find the field * referenced in the query string and apply its sort callback to the query. * * @param Fields $fields * @param Select $select * @throws \Dewdrop\Fields\Exception * @return Select */ public function modifySelect(Fields $fields, Select $select) { $this->sortedField = null; $this->sortedDirection = null; /* @var $field FieldInterface */ foreach ($fields->getSortableFields() as $field) { if ($field->getQueryStringId() === urlencode($this->request->getQuery($this->prefix . 'sort'))) { if ('ASC' === $this->defaultDirection) { $dir = 'DESC' === strtoupper($this->request->getQuery($this->prefix . 'dir')) ? 'DESC' : 'ASC'; } else { $dir = 'ASC' === strtoupper($this->request->getQuery($this->prefix . 'dir')) ? 'ASC' : 'DESC'; } $select = call_user_func($this->getFieldAssignment($field), $select, $dir); if (!$select instanceof Select) { throw new Exception('Your SelectSort callback must return the modified Select object.'); } $this->sortedField = $field; $this->sortedDirection = $dir; return $select; } } // Sort by the first visible field that is also sortable, if no other sort was performed foreach ($fields->getVisibleFields() as $field) { if ($field->isSortable() && (null === $this->defaultField || $this->defaultField === $field)) { $this->sortedField = $field; $this->sortedDirection = $this->defaultDirection; return call_user_func($this->getFieldAssignment($field), $select, $this->defaultDirection); } } return $select; }
/** * Render the filter controls inside a form tag that is rendered elsewhere. * No form HTML tags or buttons will be rendered by this method, only the * filter controls themselves. * * By default, this form will use GET so that it geneates a query string * that can be used to share search results, but you can use POST if needed * for your case. * * @param Fields $fields * @param SelectFilter $selectFilter * @param string $title * @param string $method * @param boolean $buttons * @return string */ public function inline(Fields $fields, SelectFilter $selectFilter, $title, $method = 'GET', $buttons = false) { $this->view->headScript()->appendFile($this->view->bowerUrl('/dewdrop/www/js/filter/main.js')); $this->view->headLink()->appendStylesheet($this->view->bowerUrl('/dewdrop/www/css/filter.css')); return $this->partial('bootstrap-filter-controls.phtml', array('fields' => $fields->getFilterableFields(), 'typeHelper' => $selectFilter->getFilterTypeHelper(), 'values' => $selectFilter->getSelectModifier()->getCurrentFilters(), 'defaultVars' => $selectFilter->getDefaultVarsHelper(), 'title' => $title, 'method' => $method, 'paramPrefix' => $selectFilter->getPrefix(), 'showButtons' => $buttons)); }
/** * Get any fields that return true when the supplied method is called and pass * the supplied filters. Note that you can either pass a single * \Dewdrop\Fields\Filter or an array of them. * * @param string $fieldMethodName * @param mixed $filters * @return Fields */ protected function getFieldsPassingMethodCheck($fieldMethodName, $filters) { $fields = new Fields([], $this->user); foreach ($this->fields as $field) { if ($field->{$fieldMethodName}($this->user)) { $fields->add($field); } } return $this->applyFilters($fields, $filters); }
public function testAddingFieldToAdditionalFieldsetDoesNotOverrideOriginalSet() { $orig = new Fields(); $orig->add($this->field); $second = new Fields(); $second->add($this->field); $this->field->add('test'); $this->assertTrue($orig->has('test')); $this->assertEquals(2, count($orig)); }
/** * Get a model from the fields object by its model name. * * @throws Exception * @param string $modelName * @return \Dewdrop\Db\Table */ public function getModel($modelName) { $models = $this->fields->getModelsByName(); if (!isset($models[$modelName])) { throw new Exception("Could not find model with name '{$modelName}'"); } return $models[$modelName]; }
/** * Add password and confirmation fields to the given fields collection * * @param Fields $fields * @return Component */ protected function addPasswordFields(Fields $fields) { static $passwordFieldName = 'password', $confirmFieldName = 'confirm_password', $minimumLength = 6; $request = $this->getRequest(); $fields->add($passwordFieldName)->assignHelperCallback('EditControl.Control', function ($helper, View $view) use($passwordFieldName, $request) { return $view->inputText(['name' => $passwordFieldName, 'type' => 'password', 'value' => $request->getPost($passwordFieldName)]); })->assignHelperCallback('InputFilter', function () use($passwordFieldName, $minimumLength) { $input = new Input($passwordFieldName); $input->setRequired(true)->getValidatorChain()->attach(new StringLength(['min' => $minimumLength])); return $input; })->setLabel('Password')->setEditable(true); $this->fields->add('confirm_password')->assignHelperCallback('EditControl.Control', function ($helper, View $view) use($confirmFieldName, $request) { return $view->inputText(['name' => $confirmFieldName, 'type' => 'password', 'value' => $request->getPost($confirmFieldName)]); })->assignHelperCallback('InputFilter', function () use($request, $passwordFieldName, $confirmFieldName) { $input = new Input($confirmFieldName); $passwordsMatchValidator = new Callback(['callback' => function ($value) use($request, $passwordFieldName) { return $value === $request->getPost($passwordFieldName); }]); $passwordsMatchValidator->setMessage('Passwords do not match.'); $input->setRequired(true)->getValidatorChain()->attach($passwordsMatchValidator); return $input; })->setLabel('Confirm Password')->setEditable(true); return $this; }
/** * Provide the Fields and Listing objects that will be tested. * * @param Fields $fields * @param Listing $listing * @throws Exception */ public function __construct(Fields $fields, Listing $listing) { $this->fields = $fields->getSortableFields(); $this->listing = $listing; $this->testSortHelperPresence(); }
/** * Render the supplied non-grouped fields in a Bootstrap table. * * @param Fields $fields * @param array $data * @param Renderer $renderer * @return string */ protected function renderFields(Fields $fields, array $data, Renderer $renderer) { $rowIndex = 0; $out = '<div class="table-responsive">'; $out .= '<table class="table">'; foreach ($fields->getVisibleFields() as $field) { $out .= '<tr>'; $content = $renderer->getContentRenderer()->render($field, $data, $rowIndex, 1); $classNames = $renderer->getTdClassNamesRenderer()->render($field, $data, $rowIndex, 1); $header = $renderer->getHeaderRenderer()->render($field); if (false !== stripos($content, '<table ')) { $out .= $this->renderTableContentInPanel($header, $classNames, $content); } else { $out .= $this->renderHeaderAndContent($header, $classNames, $content); } $out .= '</tr>'; $rowIndex += 1; } $out .= '</table>'; $out .= '</div>'; return $out; }