From 3bb71daefdb8b2cc0ac482586616b526692a9909 Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Wed, 21 Jan 2015 19:09:31 +0100 Subject: [PATCH 01/15] Add support for pagination of entries --- README.markdown | 20 +++++++++++++++++++- assets/order_entries.publish.js | 19 ++++++++++++++----- extension.driver.php | 26 +++++++++++++++++++++++++- extension.meta.xml | 3 +++ fields/field.order_entries.php | 12 ++++++++++++ 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index eddbbcf..1fe9213 100644 --- a/README.markdown +++ b/README.markdown @@ -10,4 +10,22 @@ 1. Add the "Entry Order" field to your section and tick the "Show column" box. 2. **Click the column heading to sort table** by the Entry Order field to enable drag and drop. -3. When ordered ascending, drag entries within the table and the orders will be re-saved. \ No newline at end of file +3. When ordered ascending, drag entries within the table and the orders will be re-saved. + +## Pagination + +As of version 2.1.4 Order entries has an additional pagination order per entry. Through this one can set a particular section to be paginated or not. + +When using normal pagination the extension makes sure that entries within the page start with the offset of that particular page. +For Example if you're on page 3 with 20 entries per page, the first entry will take a minimum index of 41. As this is the least possible to be after the last one of the previous page. + +## Filtered Views + +Since Filtering made it to the core, some problems have arisen in regards to Order Entries. In it's previous form filtering and re-ordering would reset the index of that particular sort to start from 1. +This is not always the expected result, with the changes implemented in this version, the offset will start from the **minimum** index found in the entry. +So if you're sorting entries which were numbered `4`, `5` and `6` they will keep the same index values when resorted. +However if you are sorting `4`, `6` and `12` sorting would give the following indices; `4`, `5` and `6`. + +Note that if you plan to use the sorting values only within the filtered views, and use that within your data sources this change will not impact your workflow. + +Expect some further changes in the near future in regards to filtered views and support of multiple sort values for different filters. \ No newline at end of file diff --git a/assets/order_entries.publish.js b/assets/order_entries.publish.js index 1634d87..7b7cf46 100644 --- a/assets/order_entries.publish.js +++ b/assets/order_entries.publish.js @@ -7,7 +7,7 @@ }); Symphony.Extensions.OrderEntries = function() { - var table, fieldId, direction, oldSorting, newSorting; + var table, fieldId, direction, oldSorting, newSorting, startValue; var init = function() { table = Symphony.Elements.contents.find('table'); @@ -30,6 +30,11 @@ // Process sort order oldSorting = getState(); + startValue = parseInt(table.find('.order-entries-item').eq(0).text()); + var assumedStartValue = Symphony.Pagination['max-rows'] * (Symphony.Pagination['current'] - 1) + 1; + if (startValue == 0 || direction == 'asc' && startValue < assumedStartValue) { + startValue = assumedStartValue; + } table.on('orderstop.orderable', processState); }; @@ -57,10 +62,12 @@ var items = table.find('.order-entries-item'); items.each(function(index) { if(direction == 'asc') { - $(this).text(index + 1); + $(this).text(index + startValue); } else { - $(this).text(items.length - index); + var largest = startValue; + if ( items.length > largest ) largest = items.length; + $(this).text(largest - index); } }); }, @@ -80,10 +87,12 @@ states = items.map(function(index) { if(direction == 'asc') { - return this.name + '=' + (index + 1); + return this.name + '=' + (index + startValue); } else { - return this.name + '=' + (items.length - index); + var largest = startValue; + if ( items.length > largest ) largest = items.length; + return this.name + '=' + (largest - index); } }).get().join('&'); diff --git a/extension.driver.php b/extension.driver.php index 1492b78..87131ab 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -54,7 +54,9 @@ public function prepareIndex($context) { // Initialise manual ordering $this->addComponents(); - $this->disablePagination(); + if ($field->get('disable_pagination') == 'yes'){ + $this->disablePagination(); + } } } } @@ -97,6 +99,19 @@ public function adjustTable($context) { * Add components for manual entry ordering */ public function addComponents() { + + $pagination = array( + 'max-rows' => Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), + 'current' => (isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1, intval($_REQUEST['pg'])) : 1) + // 'total' => $pgmax + ); + + Administration::instance()->Page->addElementToHead( + new XMLElement('script', 'Symphony.Pagination='.json_encode($pagination), array( + 'type' => 'text/javascript' + )) + ); + Administration::instance()->Page->addScriptToHead( URL . '/extensions/order_entries/assets/order_entries.publish.js' ); @@ -150,6 +165,15 @@ public function update($previousVersion) { "); } + // Prior version 2.1.4 + if(version_compare($previousVersion, '2.1.4', '<')) { + $status[] = Symphony::Database()->query(" + ALTER TABLE `tbl_fields_order_entries` + ADD `disable_pagination` enum('yes','no') + DEFAULT 'yes' + "); + } + // Report status if(in_array(false, $status, true)) { return false; diff --git a/extension.meta.xml b/extension.meta.xml index c188ac3..45540a7 100644 --- a/extension.meta.xml +++ b/extension.meta.xml @@ -18,6 +18,9 @@ + + - Add Disable Pagination Option + - Fixed the login page being crashed diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index 5f35fa0..babc86b 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -107,6 +107,17 @@ function displaySettingsPanel(&$wrapper, $errors = null) { $label->setValue(__('%s Force manual sorting', array($input->generate()))); $div->appendChild($label); + + $label = Widget::Label(); + $label->setAttribute('class', 'column'); + $input = Widget::Input("fields[{$order}][disable_pagination]", 'yes', 'checkbox'); + + if($this->get('disable_pagination') == 'yes') { + $input->setAttribute('checked', 'checked'); + } + + $label->setValue(__('%s Disable Pagination', array($input->generate()))); + $div->appendChild($label); $wrapper->appendChild($div); // Display options @@ -143,6 +154,7 @@ function commit() { $fields['field_id'] = $id; $fields['force_sort'] = $this->get('force_sort'); + $fields['disable_pagination'] = $this->get('disable_pagination'); $fields['hide'] = $this->get('hide'); // Update section's sorting field From 1a18213f0c640cb9cc8e1299b3748a974e284fce Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Fri, 23 Jan 2015 12:24:05 +0100 Subject: [PATCH 02/15] add functionality to support distinct ordering when using filters --- assets/order_entries.publish.js | 15 ++- content/content.save.php | 48 +++++++- extension.driver.php | 75 +++++++++++++ extension.meta.xml | 3 + fields/field.order_entries.php | 189 +++++++++++++++++++++++++++++++- 5 files changed, 317 insertions(+), 13 deletions(-) diff --git a/assets/order_entries.publish.js b/assets/order_entries.publish.js index 7b7cf46..fe388a8 100644 --- a/assets/order_entries.publish.js +++ b/assets/order_entries.publish.js @@ -7,12 +7,21 @@ }); Symphony.Extensions.OrderEntries = function() { - var table, fieldId, direction, oldSorting, newSorting, startValue; + var table, fieldId, direction, oldSorting, newSorting, startValue, filters; var init = function() { table = Symphony.Elements.contents.find('table'); fieldId = table.attr('data-order-entries-id'); direction = table.attr('data-order-entries-direction'); + filters = Symphony.Context.get('env').filters; + + // convert filters into a query string + if (typeof(filters)!== 'undefined'){ + filters = {"filters":filters}; + filters = '&' + $.param(filters) + } else { + filters = ''; + } // Add help Symphony.Elements.breadcrumbs.append('

– ' + Symphony.Language.get('drag to reorder') + '

'); @@ -54,11 +63,11 @@ $.ajax({ type: 'GET', url: Symphony.Context.get('symphony') + '/extension/order_entries/save/', - data: newSorting + '&field=' + fieldId + '&' + Symphony.Utilities.getXSRF(true), + data: newSorting + '&field=' + fieldId + filters + '&' + Symphony.Utilities.getXSRF(true), success: function() { oldSorting = newSorting; - // Update indexes + // Update indexes var items = table.find('.order-entries-item'); items.each(function(index) { if(direction == 'asc') { diff --git a/content/content.save.php b/content/content.save.php index 97aded0..0c02320 100644 --- a/content/content.save.php +++ b/content/content.save.php @@ -7,12 +7,39 @@ public function view(){ $field_id = General::sanitize($_GET['field']); $items = $_GET['items']; + $filters = $_GET['filters']; if(!is_array($items) || empty($items)) { $this->_Result['error'] = __('No items provided'); $this->generate(); }; + $where = ''; + + $field = FieldManager::fetch($field_id); + $filterableFields = explode(',', $field->get('filtered_fields')); + $section_id = $field->get('parent_section'); + + if(!is_array($filters) && empty($filters)) { + $filters = array(); + } + + //set change the field name with the field id for each filter + if(!empty($filterableFields)) { + + // check if the filters are related to the entry order being saved + foreach ($filters as $field_name => $value) { + $filtered_field_id = FieldManager::fetchFieldIDFromElementName($field_name,$section_id); + if (in_array($filtered_field_id, $filterableFields)){ + $filters[$filtered_field_id] = $value; + + } + unset($filters[$field_name]); + } + + $where = $field->buildFilteringSQL($filters); + } + /** * Just prior to reordering entries * @@ -28,25 +55,36 @@ public function view(){ $id = Symphony::Database()->fetchVar('id', 0, " SELECT id FROM tbl_entries_data_$field_id - WHERE `entry_id` = '$entry_id' + WHERE `entry_id` = '$entry_id' {$where} ORDER BY id ASC LIMIT 1 "); if(is_null($id)) { + $fields = ''; + $values = ''; + + //add the filtered params if available (default set to null) + foreach ($filterableFields as $key => $filterable_field) { + if (isset($filters[$filterable_field])){ + $fields .= " ,field_{$filterable_field}"; + $values .= " ,'{$filters[$filterable_field]}'"; + } + } + Symphony::Database()->query(" - INSERT INTO tbl_entries_data_$field_id (entry_id, value) - VALUES ('$entry_id', '$position') + INSERT INTO tbl_entries_data_$field_id (entry_id, value{$fields}) + VALUES ('$entry_id', '$position'{$values}) "); } else { Symphony::Database()->query(" UPDATE tbl_entries_data_$field_id SET `value`='$position' - WHERE `entry_id` = '$entry_id' + WHERE `entry_id` = '$entry_id' {$where} "); Symphony::Database()->query(" DELETE FROM tbl_entries_data_$field_id - WHERE `entry_id` = '$entry_id' + WHERE `entry_id` = '$entry_id' {$where} AND `id` > '$id' "); } diff --git a/extension.driver.php b/extension.driver.php index 87131ab..e5e3c5a 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -6,6 +6,7 @@ private $force_sort = false; private $field_id = 0; private $direction = 'asc'; + private $dsFilters; /** * {@inheritDoc} @@ -26,10 +27,59 @@ public function getSubscribedDelegates(){ 'page' => '/backend/', 'delegate' => 'AdminPagePostGenerate', 'callback' => 'resetPagination' + ), + array( + 'page' => '/frontend/', + 'delegate' => 'DataSourcePreExecute', + 'callback' => 'saveFilterContext' ) ); } + /** + * Save the Datasource Filter Context so it can be used for ordering + */ + public function saveFilterContext($context) { + $this->dsFilters = $context['datasource']->dsParamFILTERS; + } + + /** + * get filters using filterable field ids and the section denoting where the filters are + */ + public function getFilters($filterableFields,$section_id){ + //if no need to filter return empty filters + if (empty($filterableFields)) return array(); + + if (isset(Symphony::Engine()->Page)){ + $context = Symphony::Engine()->Page->getContext(); + $filters = $context['filters']; + if (!isset($filters)) $filters = array(); + + // check if the filters are used for entry ordering and switch from name to id + foreach ($filters as $field_name => $value) { + $filtered_field_id = FieldManager::fetchFieldIDFromElementName($field_name,$section_id); + if (in_array($filtered_field_id, $filterableFields)){ + $filters[$filtered_field_id] = $value; + } + unset($filters[$field_name]); + } + + } else { + $filters = $this->dsFilters; + + // check if the filters are used for entry ordering otherwise remove from list + foreach ($filters as $filtered_field_id => $value) { + if (!in_array($filtered_field_id, $filterableFields)){ + unset($filters[$filtered_field_id]); + } + } + + } + + return $filters; + } + + /** * Prepare publish index for manual entry ordering */ @@ -174,6 +224,29 @@ public function update($previousVersion) { "); } + // Prior version 2.2 + if(version_compare($previousVersion, '2.2', '<')) { + $status[] = Symphony::Database()->query(" + ALTER TABLE `tbl_fields_order_entries` + ADD `filtered_fields` varchar(255) DEFAULT NULL + DEFAULT NULL + "); + + $fields = Symphony::Database()->fetchCol('field_id',"SELECT field_id FROM `tbl_fields_order_entries`"); + + foreach ($fields as $key => $field) { + $status[] = Symphony::Database()->query(" + ALTER TABLE `tbl_entries_data_{$field}` + DROP INDEX `entry_id` + "); + + $status[] = Symphony::Database()->query(" + ALTER TABLE `tbl_entries_data_{$field}` + ADD UNIQUE `unique`(`entry_id`) + "); + } + } + // Report status if(in_array(false, $status, true)) { return false; @@ -193,6 +266,8 @@ public function install() { `field_id` int(11) unsigned NOT NULL, `force_sort` enum('yes','no') default 'no', `hide` enum('yes','no') default 'no', + `disable_pagination` enum('yes','no') default 'no', + `filtered_fields` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `field_id` (`field_id`) ) TYPE=MyISAM diff --git a/extension.meta.xml b/extension.meta.xml index 45540a7..d619c6f 100644 --- a/extension.meta.xml +++ b/extension.meta.xml @@ -18,6 +18,9 @@ + + - Add contextual Ordering using backend/frontend filters + - Add Disable Pagination Option diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index babc86b..df79daf 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -138,6 +138,91 @@ function displaySettingsPanel(&$wrapper, $errors = null) { $this->appendShowColumnCheckbox($div); $fieldset->appendChild($div); $wrapper->appendChild($fieldset); + + //filtered orders + + $fieldset = new XMLElement('fieldset'); + + $div = new XMLElement('h3', __('Filtered Ordering')); + $fieldset->appendChild($div); + + $section = SectionManager::fetch($this->get('parent_section')); + if (!is_object($section)){ + // you need to save first + $div = new XMLElement('p', __('You have to save this field before you can add filtered ordering')); + $fieldset->appendChild($div); + } else { + $fields = $section->fetchFields(); + + $options = array(); + + $filteredFields = $this->get('filtered_fields'); + if (!is_array($filteredFields)){ + $filteredFields = explode(',', $filteredFields); + } + + if (is_array($fields)) { + foreach ($fields as $field) { + $selected = in_array($field->get('id'), $filteredFields); + $options[] = array($field->get('id'),$selected,$field->get('label')); + } + } + + $label = Widget::Label(__('Fields')); + $label->appendChild( + Widget::Select("fields[{$order}][filtered_fields][]", $options, array( + 'multiple' => 'multiple', + 'data-required' => 'false' + )) + ); + + $fieldset->appendChild($label); + + } + + $wrapper->appendChild($fieldset); + + + } + + private function updateFilterTable(){ + $filteredFields = $this->get('filtered_fields'); + if (!is_array($filteredFields)){ + $filteredFields = explode(',', $filteredFields); + } + + $orderFieldId = $this->get('id'); + + // fetch existing table schema + $currentFilters = Symphony::Database()->fetchCol('Field',"SHOW COLUMNS FROM tbl_entries_data_{$orderFieldId} WHERE Field like 'field_%';"); + + //change the value format to match the filtered fields stored + foreach ($currentFilters as $key => $value) { + $currentFilters[$key] = substr($value, 6); + } + + $newFilters = array_diff($filteredFields, $currentFilters); + $removedFilters = array_diff($currentFilters, $filteredFields); + + foreach ($removedFilters as $key => $field_id) { + Symphony::Database()->query("ALTER TABLE `tbl_entries_data_{$orderFieldId}` DROP COLUMN `field_{$field_id}`"); + } + + foreach ($newFilters as $key => $field_id) { + //maybe in the future fields can give supported filters until then using a varchar for flexibility + $fieldtype = "varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL"; + + Symphony::Database()->query("ALTER TABLE `tbl_entries_data_{$orderFieldId}` ADD COLUMN `field_{$field_id}`{$fieldtype}"); + } + + if (!empty($newFilters) || !empty($removedFilters)){ + $fields = ''; + foreach ($filteredFields as $field_id) { + $fields .= ",`field_{$field_id}` "; + } + Symphony::Database()->query("ALTER TABLE `tbl_entries_data_{$orderFieldId}` DROP INDEX `unique`;"); + Symphony::Database()->query("ALTER TABLE `tbl_entries_data_{$orderFieldId}` ADD UNIQUE `unique`(`entry_id` {$fields});"); + } } function commit() { @@ -155,6 +240,7 @@ function commit() { $fields['field_id'] = $id; $fields['force_sort'] = $this->get('force_sort'); $fields['disable_pagination'] = $this->get('disable_pagination'); + $fields['filtered_fields'] = implode(',', $this->get('filtered_fields')); $fields['hide'] = $this->get('hide'); // Update section's sorting field @@ -163,6 +249,8 @@ function commit() { $section->setSortingField($id); } + $this->updateFilterTable(); + Symphony::Database()->query("DELETE FROM `tbl_fields_".$this->handle()."` WHERE `field_id` = '$id' LIMIT 1"); return Symphony::Database()->insert($fields, 'tbl_fields_' . $this->handle()); } @@ -228,7 +316,7 @@ public function createTable() { `entry_id` int(11) unsigned NOT null, `value` double default null, PRIMARY KEY (`id`), - UNIQUE KEY `entry_id` (`entry_id`), + UNIQUE KEY `unique` (`entry_id`), KEY `value` (`value`) ) TYPE=MyISAM; "); @@ -252,22 +340,113 @@ function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false) { return true; } + public function buildSortingSQL(&$joins, &$where, &$sort, $order = 'ASC') { + + $filterableFields = explode(',', $this->get('filtered_fields')); + $section_id = $this->get('parent_section'); + + $orderEntriesExtension = ExtensionManager::create('order_entries'); + $filters = $orderEntriesExtension->getFilters($filterableFields,$section_id); + + $filteringParams = $this->buildFilteringSQL($filters,'`ed`.'); + + if (in_array(strtolower($order), array('random', 'rand'))) { + $sort = 'ORDER BY RAND()'; + } else { + $joins .= "LEFT OUTER JOIN `tbl_entries_data_".$this->get('id')."` AS `ed` ON (`e`.`id` = `ed`.`entry_id`{$filteringParams}) "; + $sort = 'ORDER BY `ed`.`value` ' . $order; + } + } + + public function buildFilteringSQL($filters,$prefix =''){ + + $filterableFields = $this->get('filtered_fields'); + + //no filters no sql to add + if (empty($filterableFields)) return ""; + + $filterableFields = explode(',', $filterableFields); + + $where = '' ; + + foreach ($filterableFields as $key => $filterable_field) { + if (isset($filters[$filterable_field])){ + $where .= " AND {$prefix}field_{$filterable_field} = '{$filters[$filterable_field]}'"; + } else { + $where .= " AND {$prefix}field_{$filterable_field} is NULL"; + } + } + + return $where; + } + + private function getOrderValue($data){ + + $filterableFields = $this->get('filtered_fields'); + + //there are no filters to apply so should just be a single value + if (empty($filterableFields)) return $data['value']; + + $filterableFields = explode(',', $filterableFields); + $section_id = $this->get('parent_section'); + + $orderEntriesExtension = ExtensionManager::create('order_entries'); + $filters = $orderEntriesExtension->getFilters($filterableFields,$section_id); + + + if (!is_array($data['value'])){ + foreach ($data as $key => $value) { + $data[$key] = array($value); + } + } + + if (is_array($data['value'])){ + $keys = array_keys($data['value']); + + foreach ($filterableFields as $filtered_field_id) { + $filter = $filters[$filtered_field_id]; + $matchingKeys = array_search($filter, $data['field_' . $filtered_field_id]); + + if (empty($matchingKeys) && !is_int($matchingKeys)){ + $matchingKeys = array(); + } else if (!is_array($matchingKeys)) { + $matchingKeys = array($matchingKeys); + } + + //intersect the original keys with the filtered ones which match the search - should leave one or no items + $keys = array_intersect($keys, $matchingKeys); + } + + if ( empty($keys) ){ + //this view is not sorted + return 0; + } else { + return $data['value'][current($keys)]; + } + } else { + return 0; + } + } + public function prepareTableValue($data, XMLElement $link = null) { + + $orderValue = $this->getOrderValue($data); + if(!$link) { - return sprintf('%d', $data['value']); + return sprintf('%d', $orderValue); } else { - $link->setValue($data['value']); + $link->setValue($orderValue); return $link->generate(); } } public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null) { - $wrapper->appendChild(new XMLElement($this->get('element_name'), $data['value'])); + $wrapper->appendChild(new XMLElement($this->get('element_name'), $this->getOrderValue($data) )); } public function getParameterPoolValue(Array $data) { - return $data['value']; + return $this->getOrderValue($data); } } From ed7ff86bb6f3efab1a8b6790ee8e991a47738ae5 Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Wed, 28 Jan 2015 18:20:30 +0100 Subject: [PATCH 03/15] some fixes + suggestions by @nitriques --- assets/order_entries.publish.js | 2 +- content/content.save.php | 2 +- extension.driver.php | 1 + fields/field.order_entries.php | 9 +++++++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assets/order_entries.publish.js b/assets/order_entries.publish.js index fe388a8..84ba01b 100644 --- a/assets/order_entries.publish.js +++ b/assets/order_entries.publish.js @@ -16,7 +16,7 @@ filters = Symphony.Context.get('env').filters; // convert filters into a query string - if (typeof(filters)!== 'undefined'){ + if (filters){ filters = {"filters":filters}; filters = '&' + $.param(filters) } else { diff --git a/content/content.save.php b/content/content.save.php index 0c02320..c37554a 100644 --- a/content/content.save.php +++ b/content/content.save.php @@ -31,7 +31,7 @@ public function view(){ foreach ($filters as $field_name => $value) { $filtered_field_id = FieldManager::fetchFieldIDFromElementName($field_name,$section_id); if (in_array($filtered_field_id, $filterableFields)){ - $filters[$filtered_field_id] = $value; + $filters[$filtered_field_id] = General::sanitize($value); } unset($filters[$field_name]); diff --git a/extension.driver.php b/extension.driver.php index e5e3c5a..c775e32 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -66,6 +66,7 @@ public function getFilters($filterableFields,$section_id){ } else { $filters = $this->dsFilters; + if (empty($filters)) return array(); // check if the filters are used for entry ordering otherwise remove from list foreach ($filters as $filtered_field_id => $value) { diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index df79daf..0d662f6 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -256,7 +256,7 @@ function commit() { } function displayPublishPanel(&$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null) { - $value = $data['value']; + $value = $this->getOrderValue($data); $label = Widget::Label($this->get('label')); if($this->get('required') != 'yes') $label->appendChild(new XMLElement('i', __('Optional'))); @@ -405,7 +405,12 @@ private function getOrderValue($data){ foreach ($filterableFields as $filtered_field_id) { $filter = $filters[$filtered_field_id]; - $matchingKeys = array_search($filter, $data['field_' . $filtered_field_id]); + + if (isset($data['field_' . $filtered_field_id])){ + $matchingKeys = array_search($filter, $data['field_' . $filtered_field_id]); + } else { + $matchingKeys = array(); + } if (empty($matchingKeys) && !is_int($matchingKeys)){ $matchingKeys = array(); From 66173881b20cf0d1b8cb3e57c59af71e40d32ac3 Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Fri, 30 Jan 2015 13:39:58 +0100 Subject: [PATCH 04/15] update readme --- README.markdown | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 1fe9213..92d0703 100644 --- a/README.markdown +++ b/README.markdown @@ -28,4 +28,13 @@ However if you are sorting `4`, `6` and `12` sorting would give the following in Note that if you plan to use the sorting values only within the filtered views, and use that within your data sources this change will not impact your workflow. -Expect some further changes in the near future in regards to filtered views and support of multiple sort values for different filters. \ No newline at end of file +## Filtered Ordering + +Order Entries now supports an option to filter entries and order them separately. +To activate this functionality you have to select which fields you would like to use for filtering from within the settings panel. +Once saved, filtered views will have separate orders depending on your settings. + +It is very important to note that with version 2.2 the "Filter value" used within the publish page matches whatever you put in your datasource. +Sorting and order values are determined from the matching datasource parameter. +If in the publish page was filtered by "Home" and your datasource filter says "home" the datasource ordering outputs will not match. +An update to tackle this more comprehensively will be released shortly. \ No newline at end of file From 92667f74f8f7e511db88e47746fe47209be2f289 Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Thu, 19 Feb 2015 15:20:22 +0100 Subject: [PATCH 05/15] ignore case for ordering --- content/content.save.php | 3 +-- extension.driver.php | 3 ++- fields/field.order_entries.php | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/content/content.save.php b/content/content.save.php index c37554a..0cbd6d8 100644 --- a/content/content.save.php +++ b/content/content.save.php @@ -31,8 +31,7 @@ public function view(){ foreach ($filters as $field_name => $value) { $filtered_field_id = FieldManager::fetchFieldIDFromElementName($field_name,$section_id); if (in_array($filtered_field_id, $filterableFields)){ - $filters[$filtered_field_id] = General::sanitize($value); - + $filters[$filtered_field_id] = strtolower(General::sanitize($value)); } unset($filters[$field_name]); } diff --git a/extension.driver.php b/extension.driver.php index c775e32..799c25b 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -59,7 +59,8 @@ public function getFilters($filterableFields,$section_id){ foreach ($filters as $field_name => $value) { $filtered_field_id = FieldManager::fetchFieldIDFromElementName($field_name,$section_id); if (in_array($filtered_field_id, $filterableFields)){ - $filters[$filtered_field_id] = $value; + //ensuring that capitalization will never be an issue + $filters[$filtered_field_id] = strtolower($value); } unset($filters[$field_name]); } diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index 0d662f6..ac0e205 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -235,12 +235,16 @@ function commit() { return false; } + $filteredFields = $this->get('filtered_fields'); + if (!isset($filteredFields)) + $filteredFields = array(); + $fields = array(); $fields['field_id'] = $id; $fields['force_sort'] = $this->get('force_sort'); $fields['disable_pagination'] = $this->get('disable_pagination'); - $fields['filtered_fields'] = implode(',', $this->get('filtered_fields')); + $fields['filtered_fields'] = implode(',', $filteredFields); $fields['hide'] = $this->get('hide'); // Update section's sorting field From 3a1d2639271335467c24225e0517d5f5baa482d3 Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Mon, 13 Apr 2015 15:03:09 +0200 Subject: [PATCH 06/15] do not save orders from entry when there are filters --- extension.driver.php | 2 +- fields/field.order_entries.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/extension.driver.php b/extension.driver.php index 799c25b..2978617 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -161,7 +161,7 @@ public function addComponents() { Administration::instance()->Page->addElementToHead( new XMLElement('script', 'Symphony.Pagination='.json_encode($pagination), array( 'type' => 'text/javascript' - )) + )) ); Administration::instance()->Page->addScriptToHead( diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index ac0e205..bc77daf 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -40,7 +40,26 @@ function processRawFieldData($data, &$status, &$message = null, $simulate = fals $status = self::__OK__; $increment_subsequent_order = false; + $filters = Symphony::Database()->fetchCol('Field',"SHOW COLUMNS FROM tbl_entries_data_{$this->get('id')} WHERE Field like 'field_%';"); + // for now if there are any filters completely ignore any override. + if (!empty($filters)){ + $filterString = implode(',', $filters); + $current_values = Symphony::Database()->fetch(" + SELECT value, {$filterString} + FROM tbl_entries_data_{$this->get('id')} + WHERE entry_id=".$entry_id." + "); + $result= array(); + foreach ($current_values as $key => $row) { + foreach ($row as $col => $value) { + $result[$col][$key] = $value; + } + } + return $result; + } + if($entry_id) { + $new_value = $data; $current_value = Symphony::Database()->fetchVar("value", 0, " SELECT value From 709edb3b8cd563377c9bce48da42dbb5f5049363 Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Sat, 23 May 2015 11:48:09 +0200 Subject: [PATCH 07/15] make filtering branch independent of core changes. Also use context so if core provides values they are not overwritten by extension --- assets/order_entries.publish.js | 4 ++-- extension.driver.php | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/assets/order_entries.publish.js b/assets/order_entries.publish.js index 84ba01b..16aa386 100644 --- a/assets/order_entries.publish.js +++ b/assets/order_entries.publish.js @@ -39,8 +39,8 @@ // Process sort order oldSorting = getState(); - startValue = parseInt(table.find('.order-entries-item').eq(0).text()); - var assumedStartValue = Symphony.Pagination['max-rows'] * (Symphony.Pagination['current'] - 1) + 1; + startValue = parseInt(table.find('.order-entries-item').eq(0).text(),10); + var assumedStartValue = Symphony.Context.get('env').pagination['max-rows'] * (Symphony.Context.get('env').pagination['current'] - 1) + 1; if (startValue == 0 || direction == 'asc' && startValue < assumedStartValue) { startValue = assumedStartValue; } diff --git a/extension.driver.php b/extension.driver.php index 2978617..f282882 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -152,16 +152,32 @@ public function adjustTable($context) { */ public function addComponents() { + // get pagination data $pagination = array( 'max-rows' => Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), 'current' => (isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1, intval($_REQUEST['pg'])) : 1) - // 'total' => $pgmax ); + + // get filter data + $filters = $_REQUEST['filter']; + if (is_array($filters)){ + $generatedFilters = array(); + foreach ($filters as $field => $value) { + $generatedFilters[$field] = $value; + } + } + + // add pagination and filter data into symphony context if Symphony does not provide it Administration::instance()->Page->addElementToHead( - new XMLElement('script', 'Symphony.Pagination='.json_encode($pagination), array( - 'type' => 'text/javascript' - )) + new XMLElement( + 'script', + 'if (! Symphony.Context.get(\'env\').pagination) Symphony.Context.get(\'env\').pagination='.json_encode($pagination).';' . + 'if (! Symphony.Context.get(\'env\').filters) Symphony.Context.get(\'env\').filters='.json_encode($generatedFilters).';' + , array( + 'type' => 'text/javascript' + ) + ) ); Administration::instance()->Page->addScriptToHead( From 30440a66f2ae8ed0926a7c7ddebb1e61df1b18b2 Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Tue, 23 Jun 2015 15:42:34 -0400 Subject: [PATCH 08/15] whitespace and format --- fields/field.order_entries.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index 91a331d..cce7bb6 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -41,9 +41,16 @@ function processRawFieldData($data, &$status, &$message = null, $simulate = fals $status = self::__OK__; $increment_subsequent_order = false; - $filters = Symphony::Database()->fetchCol('Field',"SHOW COLUMNS FROM tbl_entries_data_{$this->get('id')} WHERE Field like 'field_%';"); + $filters = Symphony::Database()->fetchCol('Field', + "SHOW COLUMNS FROM tbl_entries_data_{$this->get('id')} WHERE Field like 'field_%';" + ); + + if ($entry_id != null) { + $entry_id = General::intval($entry_id); + } + // for now if there are any filters completely ignore any override. - if (!empty($filters)){ + if (!empty($filters) && $entry_id){ $filterString = implode(',', $filters); $current_values = Symphony::Database()->fetch(" SELECT value, {$filterString} @@ -60,7 +67,6 @@ function processRawFieldData($data, &$status, &$message = null, $simulate = fals } if($entry_id) { - $new_value = $data; $current_value = Symphony::Database()->fetchVar("value", 0, " SELECT value @@ -231,7 +237,6 @@ private function updateFilterTable(){ foreach ($newFilters as $key => $field_id) { //maybe in the future fields can give supported filters until then using a varchar for flexibility $fieldtype = "varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL"; - Symphony::Database()->query("ALTER TABLE `tbl_entries_data_{$orderFieldId}` ADD COLUMN `field_{$field_id}`{$fieldtype}"); } @@ -409,7 +414,9 @@ private function getOrderValue($data){ $filterableFields = $this->get('filtered_fields'); //there are no filters to apply so should just be a single value - if (empty($filterableFields)) return $data['value']; + if (empty($filterableFields)) { + return $data['value']; + } $filterableFields = explode(',', $filterableFields); $section_id = $this->get('parent_section'); From e85ef5d8c28d2fd4ffd43f184bff32e8d0677219 Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Tue, 23 Jun 2015 15:44:41 -0400 Subject: [PATCH 09/15] Removed code that broken processRawFieldData Filtering related code in processRawFieldData made the field crash if filtering was activated in the field setting. Basically, filtering infos is never available in the publish edit/new context. This made the getOrderValue function returned zero and was overriding the no-filtered value with 0 when the field instance was saved. --- fields/field.order_entries.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index cce7bb6..bfbb89c 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -48,23 +48,6 @@ function processRawFieldData($data, &$status, &$message = null, $simulate = fals if ($entry_id != null) { $entry_id = General::intval($entry_id); } - - // for now if there are any filters completely ignore any override. - if (!empty($filters) && $entry_id){ - $filterString = implode(',', $filters); - $current_values = Symphony::Database()->fetch(" - SELECT value, {$filterString} - FROM tbl_entries_data_{$this->get('id')} - WHERE entry_id=".$entry_id." - "); - $result= array(); - foreach ($current_values as $key => $row) { - foreach ($row as $col => $value) { - $result[$col][$key] = $value; - } - } - return $result; - } if($entry_id) { $new_value = $data; From 6c23a1d7acab101605b17f285f5b6fa77c576669 Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Tue, 23 Jun 2015 15:46:58 -0400 Subject: [PATCH 10/15] Better sanitization of the filter data This makes sure we do not end up with an empty filter column in the database. --- fields/field.order_entries.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index bfbb89c..b34809b 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -207,11 +207,16 @@ private function updateFilterTable(){ //change the value format to match the filtered fields stored foreach ($currentFilters as $key => $value) { - $currentFilters[$key] = substr($value, 6); + $currentFilter = substr($value, 6); + if (!empty($currentFilter)) { + $currentFilters[$key] = $currentFilter; + } else { + unset($currentFilters[$key]); + } } - $newFilters = array_diff($filteredFields, $currentFilters); - $removedFilters = array_diff($currentFilters, $filteredFields); + $newFilters = array_filter(array_diff($filteredFields, $currentFilters)); + $removedFilters = array_filter(array_diff($currentFilters, $filteredFields)); foreach ($removedFilters as $key => $field_id) { Symphony::Database()->query("ALTER TABLE `tbl_entries_data_{$orderFieldId}` DROP COLUMN `field_{$field_id}`"); From cdff87f059eef243ebf683a8957c5df7acc4c233 Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Tue, 23 Jun 2015 15:48:13 -0400 Subject: [PATCH 11/15] Added a short-circuit logic in getOrderValue This removes extra processing when there are no filters. --- fields/field.order_entries.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index b34809b..091d94a 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -412,6 +412,10 @@ private function getOrderValue($data){ $orderEntriesExtension = ExtensionManager::create('order_entries'); $filters = $orderEntriesExtension->getFilters($filterableFields,$section_id); + // if there are no filter, bail out + if (empty($filters)) { + return $data['value']; + } if (!is_array($data['value'])){ foreach ($data as $key => $value) { From 42c957215c290bdee46431759e58783cf3317b58 Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Tue, 23 Jun 2015 15:49:07 -0400 Subject: [PATCH 12/15] Return the non-filtered value when no filter When the view is not sorted. --- fields/field.order_entries.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/field.order_entries.php b/fields/field.order_entries.php index 091d94a..83f5314 100644 --- a/fields/field.order_entries.php +++ b/fields/field.order_entries.php @@ -447,7 +447,7 @@ private function getOrderValue($data){ if ( empty($keys) ){ //this view is not sorted - return 0; + return current($data['value']); } else { return $data['value'][current($keys)]; } From a04b38d2e93535e1c36643d2d58545d45053b8a3 Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Sat, 25 Jul 2015 09:05:04 +0200 Subject: [PATCH 13/15] ensure that ordering works when the sort order is hidden --- assets/order_entries.publish.js | 14 ++++++++++---- extension.driver.php | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/assets/order_entries.publish.js b/assets/order_entries.publish.js index 2933606..1cac9d6 100644 --- a/assets/order_entries.publish.js +++ b/assets/order_entries.publish.js @@ -39,7 +39,11 @@ // Process sort order oldSorting = getState(); - startValue = parseInt(table.find('.order-entries-item').eq(0).text(),10); + if (table.find('.order-entries-item').length > 0){ + startValue = parseInt(table.find('.order-entries-item').eq(0).text(),10); + } else { + startValue = parseInt(table.find('tbody tr').eq(0).data('order'),10); + } var assumedStartValue = Symphony.Context.get('env').pagination['max-rows'] * (Symphony.Context.get('env').pagination['current'] - 1) + 1; if (startValue == 0 || direction == 'asc' && startValue < assumedStartValue) { startValue = assumedStartValue; @@ -68,15 +72,17 @@ oldSorting = newSorting; // Update indexes - var items = table.find('.order-entries-item'); + var items = table.find('tbody tr'); items.each(function(index) { if(direction == 'asc') { - $(this).text(index + startValue); + $(this).data('order',index + startValue); + $(this).find('.order-entries-item').text(index + startValue); } else { var largest = startValue; if ( items.length > largest ) largest = items.length; - $(this).text(largest - index); + $(this).data('order',largest - index); + $(this).find('.order-entries-item').text(largest - index); } }); }, diff --git a/extension.driver.php b/extension.driver.php index f282882..42d08b5 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -140,6 +140,23 @@ public function adjustTable($context) { if($this->force_sort == 'yes') { $table->setAttribute('data-order-entries-force', 'true'); } + + $field = FieldManager::fetch($this->field_id); + + if ($field->get('show_column') == 'no'){ + + // sort order is not provided by field, so add manually + $tbody = $table->getChildByName('tbody',0); + + //not looping as only the first row is required for sorting and is far more efficient + $tr = $tbody->getChildByName('tr',0); + + $entry_id = str_replace('id-', '', $tr->getAttribute('id')); + $entry = current(EntryManager::fetch($entry_id)); + $data = $entry->getData($this->field_id); + $order = $field->getParameterPoolValue($data); + $tr->setAttribute('data-order',$order); + } break; } From 3e545897038bc200ba214787f9223bbfeca3789a Mon Sep 17 00:00:00 2001 From: Jon Mifsud Date: Wed, 29 Jul 2015 16:23:39 +0200 Subject: [PATCH 14/15] ensure it does not throw an error if the seciton has no order field --- extension.driver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.driver.php b/extension.driver.php index 42d08b5..906e0dc 100644 --- a/extension.driver.php +++ b/extension.driver.php @@ -143,7 +143,7 @@ public function adjustTable($context) { $field = FieldManager::fetch($this->field_id); - if ($field->get('show_column') == 'no'){ + if ($field && $field->get('show_column') == 'no'){ // sort order is not provided by field, so add manually $tbody = $table->getChildByName('tbody',0); From 34328e3852f77477f8e23500a3dfe40855b1e205 Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Mon, 10 Aug 2015 15:49:36 -0400 Subject: [PATCH 15/15] 2.3.0 release notes --- extension.meta.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extension.meta.xml b/extension.meta.xml index 9d1c22e..0a68860 100644 --- a/extension.meta.xml +++ b/extension.meta.xml @@ -18,10 +18,14 @@ - + + - Added filtered ordering + - Bug fixes + + - Fix entry selection - + - Add contextual Ordering using backend/frontend filters - Add Disable Pagination Option