diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Upsell.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Upsell.php index c62487dc770..567f9c21334 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Upsell.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Upsell.php @@ -134,6 +134,7 @@ protected function _prepareColumns() 'width' => 60, 'index' => 'entity_id' ]); + $this->addColumn('name', [ 'header' => Mage::helper('catalog')->__('Name'), 'index' => 'name' diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Grid.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Grid.php index 781ae922d15..edef8c7397d 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Grid.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Grid.php @@ -61,6 +61,7 @@ protected function _prepareCollection() 'left' ); } + if ($store->getId()) { //$collection->setStoreId($store->getId()); $adminStore = Mage_Core_Model_App::ADMIN_STORE_ID; @@ -151,6 +152,7 @@ protected function _prepareColumns() 'index' => 'entity_id', ] ); + $this->addColumn( 'name', [ diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php index f51c7a42c20..350286898e9 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php @@ -180,6 +180,26 @@ class Mage_Adminhtml_Block_Widget_Grid extends Mage_Adminhtml_Block_Widget */ protected $_massactionBlockName = 'adminhtml/widget_grid_massaction'; + /** + * Advanced Grid block name + * + * @var string + */ + protected $_advancedGridBlockName = 'adminhtml/widget_grid_advanced'; + + /** + * Advanced Grid helper name + * + * @var string + */ + protected $_advancedGridHelperName = 'adminhtml/widget_grid_config'; + + /** + * Advanced Grid helper + * + */ + protected $_advancedGridHelper = null; + /** * RSS list * @@ -274,6 +294,22 @@ public function getExportButtonHtml() return $this->getChildHtml('export_button'); } + /** + * @return string + */ + public function getResetColumnsButtonHtml() + { + return $this->getChildHtml('reset_columns_order_button'); + } + + /** + * @return string + */ + public function getToggleColumnsOrderButtonHtml() + { + return $this->getChildHtml('toggle_columns_order_button'); + } + /** * @return string */ @@ -296,6 +332,10 @@ public function getSearchButtonHtml() public function getMainButtonsHtml() { $html = ''; + if ($this->getAdvancedGridHelper()->isEnabled()) { + $html .= $this->getToggleColumnsOrderButtonHtml(); + $html .= $this->getResetColumnsButtonHtml(); + } if ($this->getFilterVisibility()) { $html .= $this->getResetFilterButtonHtml(); $html .= $this->getSearchButtonHtml(); @@ -566,6 +606,7 @@ protected function _setCollectionOrder($column) protected function _prepareCollection() { if ($this->getCollection()) { + $this->_prepareAdvancedGrid(); $this->_preparePage(); $columnId = $this->getParam($this->getVarNameSort(), $this->_defaultSort); @@ -622,13 +663,125 @@ protected function _preparePage() } /** - * Prepeare columns for grid + * Prepare columns for grid * * @return $this */ protected function _prepareColumns() { $this->sortColumnsByOrder(); + $this->_prepareAdvancedGridColumn(); + return $this; + } + + /** + * Prepare grid advanced grid block + * + * @return $this + */ + protected function _prepareAdvancedGridBlock() + { + $this->setChild('advanced_grid', $this->getLayout()->createBlock($this->getAdvancedGridBlockName())); + + $helper = $this->getAdvancedGridHelper(); + if ($helper->isEnabled() && $helper->isRearrangeEnabled()) { + $this->setChild( + 'toggle_columns_order_button', + $this->getLayout()->createBlock('adminhtml/widget_button') + ->setData([ + 'class' => 'toggle_columns_order_button', + 'label' => Mage::helper('adminhtml')->__('Rearrange Columns'), + ]) + ); + $this->setChild( + 'reset_columns_order_button', + $this->getLayout()->createBlock('adminhtml/widget_button') + ->setData([ + 'label' => Mage::helper('adminhtml')->__('Reset Columns Order'), + 'class' => 'reset_columns_order_button delete', + ]) + ); + } + return $this; + } + + /** + * Get Helper Advanced Grid + * + * @return Mage_Adminhtml_Helper_Widget_Grid_Config_Abstract + */ + public function getAdvancedGridHelper(): Mage_Adminhtml_Helper_Widget_Grid_Config_Abstract + { + if (!$this->_advancedGridHelper) { + // TODO create factory class map block id to helper - create a new grid.xml configuration file + switch ($this->getId()) { + case 'productGrid': + case 'catalog_category_products': + case 'related_product_grid': + case 'up_sell_product_grid': + case 'cross_sell_product_grid': + case 'super_product_links': + $this->setAdvancedGridHelperName('adminhtml/widget_grid_config_catalog_product'); + break; + } + $this->_advancedGridHelper = Mage::helper($this->getAdvancedGridHelperName()); + + if (!($this->_advancedGridHelper instanceof Mage_Adminhtml_Helper_Widget_Grid_Config_Abstract)) { + Mage::throwException( + $this->__("Helper for advanced grid config doesn't implement required interface. %s must extends Mage_Adminhtml_Helper_Widget_Grid_Config_Abstract", get_class($this->_advancedGridHelper)) + ); + } + } + $this->_advancedGridHelper->setGridId($this->getId()); + return $this->_advancedGridHelper; + } + + /** + * Prepare advanced grid collection + * + * @return $this + */ + protected function _prepareAdvancedGrid() + { + if (!$this->getAdvancedGridHelper()->isEnabled()) { + return $this; + } + $this->getAdvancedGridHelper()->applyAdvancedGridCollection($this->getCollection()); + return $this; + } + + /** + * Prepare advanced grid column + * + * @return $this + */ + protected function _prepareAdvancedGridColumn() + { + if (!$this->getAdvancedGridHelper()->isEnabled()) { + return $this; + } + //if ($helper instanceof CollectionStrategyInterface) { + $this->getAdvancedGridHelper()->applyAdvancedGridColumn($this); + // customize order column + $_orderColumns = $this->getAdvancedGridHelper()->getOrderColumns(); + if ($_orderColumns) { + // Reset Column Order + $this->_columnsOrder = []; + uksort($this->_columns, function ($a, $b) use ($_orderColumns) { + $posA = array_search($a, $_orderColumns); + $posB = array_search($b, $_orderColumns); + if ($posA > $posB) { + return 1; + } + if ($posA < $posB) { + return -1; + } + return 0; + }); + } else { + $this->sortColumnsByOrder(); + } + return $this; } @@ -699,6 +852,7 @@ protected function _prepareGrid() $this->_prepareColumns(); $this->_prepareMassactionBlock(); $this->_prepareCollection(); + $this->_prepareAdvancedGridBlock(); return $this; } @@ -1581,6 +1735,68 @@ public function getMassactionBlockHtml() return $this->getChildHtml('massaction'); } + /** + * Retrieve advanced grid block name + * + * @return string + */ + public function getAdvancedGridBlockName() + { + return $this->_advancedGridBlockName; + } + + /** + * Set advanced grid block name + * + * @param string $blockName + * @return $this + */ + public function setAdvancedGridBlockName($blockName) + { + $this->_advancedGridBlockName = $blockName; + return $this; + } + + /** + * Retrieve advanced grid helper name + * + * @return string + */ + public function getAdvancedGridHelperName() + { + return $this->_advancedGridHelperName; + } + + /** + * Set advanced grid helper name + * + * @param string $helperName + * @return $this + */ + public function setAdvancedGridHelperName($helperName) + { + $this->_advancedGridHelperName = $helperName; + return $this; + } + + /** + * Retrieve advanced grid block + * + * @return Mage_Adminhtml_Block_Widget_Grid_Advanced_Abstract + */ + public function getAdvancedGridBlock() + { + return $this->getChild('advanced_grid'); + } + + /** + * @return string + */ + public function getAdvancedGridBlockHtml() + { + return $this->getChildHtml('advanced_grid'); + } + /** * Set empty text for grid * diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Advanced.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Advanced.php new file mode 100644 index 00000000000..0263cb3a750 --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Advanced.php @@ -0,0 +1,24 @@ +setResetConfirmText(Mage::helper('core')->jsQuoteEscape(Mage::helper('core')->__('Are you sure?'))); + } + + /** + * @return string + */ + public function getHtmlId() + { + return $this->getParentBlock()->getHtmlId() . '_advanced'; + } + + /** + * Retrieve advanced block js object name + * + * @return mixed + */ + public function getJsObjectName(): mixed + { + return $this->getHtmlId() . 'JsObject'; + } + + /** + * Retrieve grid block js object name + * + * @return string + */ + public function getGridJsObjectName(): string + { + return $this->getParentBlock()->getJsObjectName(); + } + + /** + * Retrieve grid id + * + * @return mixed + */ + public function getGridId(): mixed + { + return $this->getParentBlock()->getId(); + } + + /** + * @return string + */ + public function getJavaScript(): string + { + // TODO: would be better to use Mage_Adminhtml_Helper_Widget_Grid_Config_Abstract::isRearrangeEnabled + if (!Mage::getStoreConfig(Mage_Adminhtml_Helper_Widget_Grid_Config_Abstract::CONFIG_PATH_ENABLE_REARRANGE_COLUMNS)) { + return ''; + } + + $_content = sprintf( + "var %s = new varienGridAdvanced('%s', %s, '%s');", + $this->getJsObjectName(), + $this->getGridId(), + $this->getGridJsObjectName(), + $this->getUrl('adminhtml/grid/saveColumnOrder') + ); + $_content .= "{$this->getJsObjectName()}.resetConfirmText = '{$this->getResetConfirmText()}';"; + return $_content; + } + + /** + * Checks are advanced grid available + * + * @return bool + */ + public function isAvailable(): bool + { + return $this->getParentBlock()->getAdvancedGridHelper()->isEnabled(); + } +} diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php index 79697d4dbc0..49041679869 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php @@ -294,6 +294,9 @@ protected function _getRendererByType() case 'theme': $rendererClass = 'adminhtml/widget_grid_column_renderer_theme'; break; + case 'productimage': + $rendererClass = 'adminhtml/widget_grid_column_renderer_productimage'; + break; default: $rendererClass = 'adminhtml/widget_grid_column_renderer_text'; break; diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Productimage.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Productimage.php new file mode 100644 index 00000000000..d7af55e5ed7 --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Productimage.php @@ -0,0 +1,105 @@ +_checkImageIsSelected($row); + if (!$image) { + return ''; + } + $imageDimensions = $this->getColumn()->getWidth() ?: $this->_defaultWidth; + $imageSrc = $this->_getHelperImage($image)->resize($imageDimensions, $imageDimensions); + $imageUrl = $this->_getImageUrl($image); + $result = ''; + $result .= ''; + $result .= '' . basename($imageUrl) . ''; + $result .= ''; + return $result; + } + + /** + * Render column for export + * + * @param Varien_Object $row + * @return string + */ + public function renderExport(Varien_Object $row): string + { + $image = $this->_checkImageIsSelected($row); + if (!$image) { + return ''; + } + return $this->_getImageUrl($image); + } + + /** + * @return string|null + */ + public function renderCss() + { + return parent::renderCss() . ' a-center'; + } + + /** + * @param string $image + * @return Mage_Catalog_Helper_Image + */ + protected function _getHelperImage($image): Mage_Catalog_Helper_Image + { + $dummyProduct = Mage::getModel('catalog/product'); + return Mage::helper('catalog/image')->init($dummyProduct, $this->getColumn()->getAttributeCode(), $image); + } + + /** + * @param string $image + * @return string + */ + protected function _getImageUrl($image): string + { + if (!$image) { + return ''; + } + return Mage::getBaseUrl('media') . 'catalog/product/' . $image; + } + + /** + * @param Varien_Object $row + * @return string|false + */ + private function _checkImageIsSelected($row) + { + $value = $this->_getValue($row); + if (!$value || $value == 'no_selection') { + return false; + } + return $value; + } +} diff --git a/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config.php b/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config.php new file mode 100644 index 00000000000..1010b32b951 --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config.php @@ -0,0 +1,45 @@ +_gridId = $id; + return $this; + } + + /** + * Get grid id configuration scope + * + * @return string + */ + public function getGridId(): string + { + return $this->_gridId; + } + + /** + * Get store config for grid id + * + * @return mixed + * @throws Mage_Core_Exception + */ + public function getStoreConfigGridId($configPath) + { + if (!$this->_gridId) { + Mage::throwException(Mage::helper('adminhtml')->__('Grid Id must be set.')); + } + $config = sprintf($configPath, $this->_gridId); + return Mage::getStoreConfig($config); + } + + + public function saveOrderColumns($value) + { + $configPath = sprintf(self::CONFIG_PATH_GRID_ORDER, $this->_gridId); + Mage::getModel('core/config')->saveConfig($configPath, $value); + } + + public function getOrderColumns(): array + { + $data = $this->getStoreConfigGridId(self::CONFIG_PATH_GRID_ORDER); + if (!$data) { + return []; + } + + $data = Mage::helper('core')->jsonDecode($data); + return $data; + } + + /** + * Get grid enabled for custom columns + * + * @return bool + */ + public function isEnabled(): bool + { + return $this->getStoreConfigGridId(self::CONFIG_PATH_GRID_ENABLED) ?: false; + } + + /** + * Get grid enabled for custom columns + * + * @return bool + */ + public function isRearrangeEnabled(): bool + { + return + Mage::getStoreConfigFlag(self::CONFIG_PATH_ENABLE_REARRANGE_COLUMNS) && + Mage::getSingleton('admin/session')->isAllowed(self::ACL_RESOURCE_REARRANGE_COLUMNS); + } + + /** + * Collection object + * @param Varien_Data_Collection_Db $collection + * + * return $this + */ + abstract public function applyAdvancedGridCollection($collection); + + /** + * Adminhtml grid widget block + * @param Mage_Adminhtml_Block_Widget_Grid $gridBlock + * + * return $this + */ + abstract public function applyAdvancedGridColumn($gridBlock); +} diff --git a/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config/Catalog/Product.php b/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config/Catalog/Product.php new file mode 100644 index 00000000000..68f6a95128d --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config/Catalog/Product.php @@ -0,0 +1,244 @@ +isEnabled()) { + return $this; + } + + if ($collection) { + foreach ($this->getAttributeColumns() as $attributeCode) { + $collection->addAttributeToSelect($attributeCode); + } + + foreach ($this->getImageColumns() as $attributeCode) { + $collection->addAttributeToSelect($attributeCode); + } + + if ($this->isCreatedAtEnabled()) { + $collection->addAttributeToSelect('created_at'); + } + + if ($this->isUpdatedAtEnabled()) { + $collection->addAttributeToSelect('updated_at'); + } + } + + return $this; + } + + /** + * Adminhtml grid widget block + * @param Mage_Adminhtml_Block_Widget_Grid $gridBlock + * + * return $this + */ + public function applyAdvancedGridColumn($gridBlock) + { + $storeId = (int) $gridBlock->getRequest()->getParam('store', 0); + $_keepDefaultOrder = 'entity_id'; + + foreach ($this->getImageColumns() as $attributeCode) { + /** @var Mage_Eav_Model_Attribute $_attributeEntity */ + $_attributeEntity = Mage::getModel('eav/entity_attribute')->loadByCode(Mage_Catalog_Model_Product::ENTITY, $attributeCode); + $gridBlock->addColumnAfter( + $attributeCode, + [ + 'header' => Mage::helper('catalog')->__($_attributeEntity->getFrontendLabel()), + 'width' => $this->getProductImageWidth(), + 'type' => 'productimage', + 'index' => $attributeCode, + 'attribute_code' => $attributeCode, + ], + $_keepDefaultOrder + ); + $_keepDefaultOrder = $attributeCode; + } + + if ($this->isCreatedAtEnabled()) { + $gridBlock->addColumnAfter( + 'created_at', + [ + 'header' => Mage::helper('catalog')->__('Created At'), + 'type' => 'datetime', + 'index' => 'created_at', + 'attribute_code' => 'created_at', + ], + $_keepDefaultOrder + ); + $_keepDefaultOrder = 'created_at'; + } + + if ($this->isUpdatedAtEnabled()) { + $gridBlock->addColumnAfter( + 'updated_at', + [ + 'header' => Mage::helper('catalog')->__('Updated At'), + 'type' => 'datetime', + 'index' => 'updated_at', + 'attribute_code' => 'updated_at', + ], + $_keepDefaultOrder + ); + $_keepDefaultOrder = 'updated_at'; + } + + foreach ($this->getAttributeColumns() as $attributeCode) { + /** @var Mage_Eav_Model_Entity_Attribute $_attributeEntity */ + $_attributeEntity = Mage::getModel('eav/entity_attribute')->loadByCode(Mage_Catalog_Model_Product::ENTITY, $attributeCode); + switch ($_attributeEntity->getFrontendInput()) { + case 'price': + $_currency = Mage::app()->getStore($storeId)->getBaseCurrency()->getCode(); + $gridBlock->addColumnAfter( + $attributeCode, + [ + 'header' => Mage::helper('catalog')->__($_attributeEntity->getFrontendLabel()), + 'type' => 'price', + 'index' => $attributeCode, + 'attribute_code' => $attributeCode, + 'currency_code' => $_currency, + ], + $_keepDefaultOrder + ); + break; + case 'date': + $gridBlock->addColumnAfter( + $attributeCode, + [ + 'header' => Mage::helper('catalog')->__($_attributeEntity->getFrontendLabel()), + 'type' => 'date', + 'index' => $attributeCode, + 'attribute_code' => $attributeCode, + ], + $_keepDefaultOrder + ); + break; + case 'multiselect': + case 'select': + $_options = []; + if ($_attributeEntity->usesSource()) { + /** @var Mage_Eav_Model_Entity_Attribute_Source_Table $_sourceModel */ + $_sourceModel = $_attributeEntity->getSource(); + $_allOptions = $_sourceModel->getAllOptions(false, true); + foreach ($_allOptions as $key => $option) { + $_options[$option['value']] = $option['label']; + } + } + $gridBlock->addColumnAfter( + $attributeCode, + [ + 'header' => Mage::helper('catalog')->__($_attributeEntity->getFrontendLabel()), + /* 'width' => '150px', */ + 'type' => 'options', + 'index' => $attributeCode, + 'options' => $_options, + 'attribute_code' => $attributeCode, + ], + $_keepDefaultOrder + ); + break; + default: + $gridBlock->addColumnAfter( + $attributeCode, + [ + 'header' => Mage::helper('catalog')->__($_attributeEntity->getFrontendLabel()), + 'type' => 'text', + 'index' => $attributeCode, + 'attribute_code' => $attributeCode, + ], + $_keepDefaultOrder + ); + break; + } + $_keepDefaultOrder = $attributeCode; + } + } + + /** + * Get column created_at is enabled + * + * @return bool + */ + public function isCreatedAtEnabled(): bool + { + return $this->getStoreConfigGridId(self::CONFIG_PATH_GRID_CREATED_AT); + } + + /** + * Get column updated_at is enabled + * + * @return bool + */ + public function isUpdatedAtEnabled(): bool + { + return $this->getStoreConfigGridId(self::CONFIG_PATH_GRID_UPDATED_AT); + } + + /** + * Get grid enabled for custom columns + * + * @return array + */ + public function getAttributeColumns(): array + { + if (!$this->getStoreConfigGridId(self::CONFIG_PATH_GRID_COLUMNS)) { + return []; + } + return explode(',', $this->getStoreConfigGridId(self::CONFIG_PATH_GRID_COLUMNS)); + } + + /** + * Get grid enabled for custom columns + * + * @return array + */ + public function getImageColumns(): array + { + if (!$this->getStoreConfigGridId(self::CONFIG_PATH_GRID_COLUMN_IMAGE)) { + return []; + } + return explode(',', $this->getStoreConfigGridId(self::CONFIG_PATH_GRID_COLUMN_IMAGE)); + } + + /** + * Get media product image width + * + * @return string + */ + public function getProductImageWidth(): string + { + return $this->getStoreConfigGridId(self::CONFIG_PATH_GRID_COLUMN_IMAGE_WIDTH); + } +} diff --git a/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config/Interface.php b/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config/Interface.php new file mode 100644 index 00000000000..d9ede263a8f --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Helper/Widget/Grid/Config/Interface.php @@ -0,0 +1,38 @@ +_attributes)) { + $attrCollection = Mage::getResourceModel('catalog/product_attribute_collection') + ->addVisibleFilter() + ->setFrontendInputTypeFilter($this->_supportedColumnsType) + ->addFieldToFilter('frontend_label', ['notnull' => true]) + ->setOrder('frontend_label', Varien_Data_Collection::SORT_ORDER_ASC); + + $this->_attributes = []; + /** @var Mage_Eav_Model_Attribute $attribute */ + foreach ($attrCollection as $attribute) { + if (!in_array($attribute->getAttributeCode(), $this->_excludeDefaultAttributesCode)) { + $this->_attributes[] = [ + 'label' => $attribute->getFrontendLabel(), + 'value' => $attribute->getAttributeCode(), + ]; + } + } + } + return $this->_attributes; + } +} diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Grid/Catalog/Product/GridIdList.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Grid/Catalog/Product/GridIdList.php new file mode 100644 index 00000000000..f3b5ec0f129 --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Grid/Catalog/Product/GridIdList.php @@ -0,0 +1,33 @@ + 'productGrid', 'label' => Mage::helper('adminhtml')->__('Catalog Product')], + ['value' => 'catalog_category_products', 'label' => Mage::helper('adminhtml')->__('Catalog Category Product')], + ['value' => 'related_product_grid', 'label' => Mage::helper('adminhtml')->__('Catalog Product Related')], + ['value' => 'up_sell_product_grid', 'label' => Mage::helper('adminhtml')->__('Catalog Product Up-Sells')], + ['value' => 'cross_sell_product_grid', 'label' => Mage::helper('adminhtml')->__('Catalog Product Cross-Sells')], + ]; + } +} diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Grid/Catalog/Product/MediaImage.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Grid/Catalog/Product/MediaImage.php new file mode 100644 index 00000000000..b2de8dace5a --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Grid/Catalog/Product/MediaImage.php @@ -0,0 +1,59 @@ +_attributes)) { + $attrCollection = Mage::getResourceModel('catalog/product_attribute_collection') + ->addVisibleFilter() + ->setFrontendInputTypeFilter($this->_supportedColumnsType) + ->addFieldToFilter('frontend_label', ['notnull' => true]) + ->setOrder('frontend_label', Varien_Data_Collection::SORT_ORDER_ASC); + + $this->_attributes = []; + /** @var Mage_Eav_Model_Attribute $attribute */ + foreach ($attrCollection as $attribute) { + $this->_attributes[] = [ + 'label' => $attribute->getFrontendLabel(), + 'value' => $attribute->getAttributeCode(), + ]; + } + } + return $this->_attributes; + } +} diff --git a/app/code/core/Mage/Adminhtml/controllers/GridController.php b/app/code/core/Mage/Adminhtml/controllers/GridController.php new file mode 100644 index 00000000000..9233c336df2 --- /dev/null +++ b/app/code/core/Mage/Adminhtml/controllers/GridController.php @@ -0,0 +1,57 @@ +getRequest()->getPost('gridId'); + $data = $this->getRequest()->getPost('data'); + $reset = $this->getRequest()->getPost('reset'); + if (!$gridId || (!$data && !$reset)) { + return false; + } + + /** @var Mage_Adminhtml_Helper_Widget_Grid_Config $_advancedGridHelper */ + $_advancedGridHelper = Mage::helper('adminhtml/widget_grid_config'); + $_advancedGridHelper + ->setGridId($gridId) + ->saveOrderColumns($data); + } + + /** + * Check is allowed access to action + * + * @return bool + */ + protected function _isAllowed() + { + return Mage::getSingleton('admin/session')->isAllowed(static::ADMIN_RESOURCE); + } +} diff --git a/app/code/core/Mage/Adminhtml/etc/adminhtml.xml b/app/code/core/Mage/Adminhtml/etc/adminhtml.xml index 3d05467b27c..d4bec256aae 100644 --- a/app/code/core/Mage/Adminhtml/etc/adminhtml.xml +++ b/app/code/core/Mage/Adminhtml/etc/adminhtml.xml @@ -205,6 +205,10 @@ Admin 100 + + Admin Grids + 101 + @@ -238,6 +242,9 @@ + + Rearrange Grids Columns + Cache Management diff --git a/app/code/core/Mage/Core/etc/config.xml b/app/code/core/Mage/Core/etc/config.xml index d7ce041219d..f8727360533 100644 --- a/app/code/core/Mage/Core/etc/config.xml +++ b/app/code/core/Mage/Core/etc/config.xml @@ -441,6 +441,32 @@ 0 + + + 0 + 64 + + + 0 + 64 + + + 0 + 64 + + + 0 + 64 + + + 0 + 64 + + + 0 + 64 + + AT,BE,BG,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,HR diff --git a/app/code/core/Mage/Core/etc/system.xml b/app/code/core/Mage/Core/etc/system.xml index 191ba8846e8..165c110b9d1 100644 --- a/app/code/core/Mage/Core/etc/system.xml +++ b/app/code/core/Mage/Core/etc/system.xml @@ -1063,7 +1063,7 @@ 1 0 0 - 6 + 7 @@ -1663,5 +1663,540 @@ + + + + advanced + 21 + 1 + 0 + 0 + + + + 1 + 1 + 0 + 0 + 1 + + + + Enable drag and drop functionality to rearrange columns order for all the grids + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + + + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 2 + 1 + 0 + 0 + 1 + + 1 + + + + + select + adminhtml/system_config_source_yesno + 3 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_column + 4 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_mediaImage + 5 + 1 + 0 + 0 + 1 + + 1 + + + + + + validate-number validate-zero-or-greater + 51 + 1 + 0 + 0 + + 1 + + + + + + + 2 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 2 + 1 + 0 + 0 + 1 + + 1 + + + + + select + adminhtml/system_config_source_yesno + 3 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_column + 4 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_mediaImage + 5 + 1 + 0 + 0 + 1 + + 1 + + + + + + validate-number validate-zero-or-greater + 51 + 1 + 0 + 0 + + 1 + + + + + + + 3 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 2 + 1 + 0 + 0 + 1 + + 1 + + + + + select + adminhtml/system_config_source_yesno + 3 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_column + 4 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_mediaImage + 5 + 1 + 0 + 0 + 1 + + 1 + + + + + + validate-number validate-zero-or-greater + 51 + 1 + 0 + 0 + + 1 + + + + + + + 4 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 2 + 1 + 0 + 0 + 1 + + 1 + + + + + select + adminhtml/system_config_source_yesno + 3 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_column + 4 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_mediaImage + 5 + 1 + 0 + 0 + 1 + + 1 + + + + + + validate-number validate-zero-or-greater + 51 + 1 + 0 + 0 + + 1 + + + + + + + 5 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 2 + 1 + 0 + 0 + 1 + + 1 + + + + + select + adminhtml/system_config_source_yesno + 3 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_column + 4 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_mediaImage + 5 + 1 + 0 + 0 + 1 + + 1 + + + + + + validate-number validate-zero-or-greater + 51 + 1 + 0 + 0 + + 1 + + + + + + + 6 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 2 + 1 + 0 + 0 + 1 + + 1 + + + + + select + adminhtml/system_config_source_yesno + 3 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_column + 4 + 1 + 0 + 0 + 1 + + 1 + + + + + + multiselect + adminhtml/system_config_source_grid_catalog_product_mediaImage + 5 + 1 + 0 + 0 + 1 + + 1 + + + + + + validate-number validate-zero-or-greater + 51 + 1 + 0 + 0 + + 1 + + + + + + diff --git a/app/design/adminhtml/default/default/layout/main.xml b/app/design/adminhtml/default/default/layout/main.xml index b2a2ef42283..4eb5444d7df 100644 --- a/app/design/adminhtml/default/default/layout/main.xml +++ b/app/design/adminhtml/default/default/layout/main.xml @@ -60,6 +60,7 @@ Default layout, loads most of the pages + diff --git a/app/design/adminhtml/default/default/template/widget/grid.phtml b/app/design/adminhtml/default/default/template/widget/grid.phtml index d24045dedc3..3d2b87c66bf 100644 --- a/app/design/adminhtml/default/default/template/widget/grid.phtml +++ b/app/design/adminhtml/default/default/template/widget/grid.phtml @@ -113,8 +113,8 @@ $numColumns = count($this->getColumns()); getHeadersVisibility()): ?> - getColumns() as $_column): ?> - getHeaderHtmlProperty() ?> data-column-id="getId() ?>">getHeaderHtml() ?> + getColumns() as $key => $_column): ?> + getHeaderHtmlProperty() ?>>getHeaderHtml() ?> @@ -213,7 +213,14 @@ $numColumns = count($this->getColumns()); getMassactionBlock()->isAvailable()): ?> getMassactionBlock()->getJavaScript() ?> + getAdvancedGridBlock()->isAvailable()): ?> + getAdvancedGridBlock()->getJavaScript() ?> + getAdditionalJavaScript(); ?> + + if (typeof gridDoubleScroll !== 'undefined') { + new gridDoubleScroll('getId() ?>', getJsObjectName() ?>); + } //]]> diff --git a/app/design/adminhtml/default/default/template/widget/grid/advanced.phtml b/app/design/adminhtml/default/default/template/widget/grid/advanced.phtml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/locale/en_US/Mage_Adminhtml.csv b/app/locale/en_US/Mage_Adminhtml.csv index c85c5f04c78..0b790a4642e 100644 --- a/app/locale/en_US/Mage_Adminhtml.csv +++ b/app/locale/en_US/Mage_Adminhtml.csv @@ -83,6 +83,7 @@ "Additional Cache Management","Additional Cache Management" "Address Type:","Address Type:" "Admin","Admin" +"Admin Grids","Admin Grids" "Advanced Profiles","Advanced Profiles" "After authorization application will have access to you account.","After authorization application will have access to you account." "Alert","Alert" @@ -225,6 +226,12 @@ "Catalog","Catalog" "Catalog Price Rules","Catalog Price Rules" "Catalog Rewrites","Catalog Rewrites" +"Catalog > Manage Products","Catalog > Manage Products" +"Catalog > Manage Products > Edit | Related Products","Catalog > Manage Products > Edit | Related Products" +"Catalog > Manage Products > Edit | Up-Sells","Catalog > Manage Products > Edit | Up-Sells" +"Catalog > Manage Products > Edit | Cross-Sells","Catalog > Manage Products > Edit | Cross-Sells" +"Catalog > Manage Products > Edit | Associated Products","Catalog > Manage Products > Edit | Associated Products" +"Catalog > Manage Categories | Category Products","Catalog > Manage Categories | Category Products" "Categories","Categories" "Category","Category" "Category:","Category:" @@ -823,6 +830,8 @@ "Products Most Viewed Report","Products Most Viewed Report" "Products Ordered","Products Ordered" "Products in Carts","Products in Carts" +"Product Image Preview","Product Image Preview" +"Product Image Width","Product Image Width" "Profile Action","Profile Action" "Profile Actions XML","Profile Actions XML" "Profile Direction","Profile Direction" @@ -845,6 +854,8 @@ "Radio Buttons","Radio Buttons" "Rates","Rates" "Read details","Read details" +"Rearrange Columns","Rearrange Columns" +"Rearrange Grids Columns","Rearrange Grids Columns" "Rebuild","Rebuild" "Rebuild Catalog Index","Rebuild Catalog Index" "Rebuild Flat Catalog Category","Rebuild Flat Catalog Category" @@ -941,6 +952,9 @@ "Selected base currency is not available in installed currencies.","Selected base currency is not available in installed currencies." "Selected default display currency is not available in allowed currencies.","Selected default display currency is not available in allowed currencies." "Selected default display currency is not available in installed currencies.","Selected default display currency is not available in installed currencies." +"Select product attributes to show as column in grid","Select product attributes to show as column in grid" +"Select product media image types","Select product media image types" +"Set product image preview width, in pixels (eg: 64)","Set product image preview width, in pixels (eg: 64)" "Self-assigned roles cannot be deleted.","Self-assigned roles cannot be deleted." "Sender","Sender" "Separate Email","Separate Email" diff --git a/js/mage/adminhtml/grid-double-scroll.js b/js/mage/adminhtml/grid-double-scroll.js new file mode 100644 index 00000000000..cce4f4356fd --- /dev/null +++ b/js/mage/adminhtml/grid-double-scroll.js @@ -0,0 +1,97 @@ +/** + * OpenMage + * + * This source file is subject to the Academic Free License (AFL 3.0) + * that is bundled with this package in the file LICENSE_AFL.txt. + * It is also available at https://opensource.org/license/afl-3-0-php + * + * @category Mage + * @package Mage_Adminhtml + * @copyright Copyright (c) 2019-2023 The OpenMage Contributors (https://www.openmage.org) + * @license https://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + */ + +class gridDoubleScroll { + oldCallbacks = {}; + _scrolling = false; + + constructor(containerId, grid) { + this.containerId = containerId; + this.grid = grid; + this.setOldCallback('init', grid.initCallback); + this.grid.initCallback = this.onGridInit.bind(this); + + this.handlerSyncWrapperScrollBar = this.syncWrapperScrollBar.bind(this); + this.handlerSyncScrollBarTop = this.syncScrollBarTop.bind(this); + this.handlerUpdateDoubleScrollWidth = this.updateDoubleScrollWidth.bind(this); + + this.initDoubleScroll(); + } + + initDoubleScroll() { + this.wrapperScrollBar = document.getElementById(this.containerId).querySelector('.hor-scroll'); + if (this.wrapperScrollBar) { + let scrollbarTop = this.wrapperScrollBar.parentNode.querySelector('.hor-scroll-top'); + if (!scrollbarTop){ + this.createDoubleScroll(this.wrapperScrollBar); + this.scrollbarTop.addEventListener('scroll', this.handlerSyncWrapperScrollBar, false); + this.wrapperScrollBar.addEventListener('scroll', this.handlerSyncScrollBarTop, false); + const observer = new MutationObserver( this.handlerUpdateDoubleScrollWidth ); + observer.observe(this.wrapperScrollBar, { childList: true, subtree: true }); + } + this.updateDoubleScrollWidth(); + } + } + + // ensure varienGrid events propagation + getOldCallback(callbackName) { + return this.oldCallbacks[callbackName] ? this.oldCallbacks[callbackName] : Prototype.emptyFunction; + } + + setOldCallback(callbackName, callback) { + this.oldCallbacks[callbackName] = callback; + } + + onGridInit(grid) { + this.initDoubleScroll(); + this.getOldCallback('init')(grid); + } + + createDoubleScroll() { + let scrollbarTop = document.createElement('div'); + scrollbarTop.classList.add('hor-scroll-top'); + scrollbarTop.appendChild(document.createElement('div')); + scrollbarTop.style.overflow = 'auto'; + scrollbarTop.style.overflowY = 'hidden'; + scrollbarTop.firstChild.style.height = '0'; + scrollbarTop.firstChild.style.paddingTop = '1px'; + scrollbarTop.firstChild.appendChild(document.createTextNode('\xA0')); + this.wrapperScrollBar.parentNode.insertBefore(scrollbarTop, this.wrapperScrollBar); + + this.scrollbarTop = scrollbarTop; + } + + syncWrapperScrollBar() { + if(this._scrolling) { + this._scrolling = false; + return; + } + this._scrolling = true; + this.wrapperScrollBar.scrollLeft = this.scrollbarTop.scrollLeft; + } + + syncScrollBarTop() { + if(this._scrolling) { + this._scrolling = false; + return; + } + this._scrolling = true; + this.scrollbarTop.scrollLeft = this.wrapperScrollBar.scrollLeft; + }; + + updateDoubleScrollWidth() { + if (this.scrollbarTop.firstChild.style.width != this.wrapperScrollBar.scrollWidth) { + this.scrollbarTop.firstChild.style.width = this.wrapperScrollBar.scrollWidth + 'px'; + } + } +}; diff --git a/js/mage/adminhtml/grid.js b/js/mage/adminhtml/grid.js index 55eb3b2ef05..8344f5f7e0d 100644 --- a/js/mage/adminhtml/grid.js +++ b/js/mage/adminhtml/grid.js @@ -202,22 +202,6 @@ varienGrid.prototype = { location.href = url; } }, - /*_processComplete : function(transport){ - console.log(transport); - if (transport && transport.responseText){ - try{ - response = eval('(' + transport.responseText + ')'); - } - catch (e) { - response = {}; - } - } - if (response.ajaxExpired && response.ajaxRedirect) { - location.href = response.ajaxRedirect; - return false; - } - this.initGrid(); - },*/ _processFailure : function(transport){ location.href = BASE_URL; }, @@ -254,7 +238,6 @@ varienGrid.prototype = { if (!$(this.containerId)) { return; } -// var dataElements = $(this.containerId+this.tableSufix).down('.data tbody').select('input', 'select'); var dataElements = $(this.containerId+this.tableSufix).down('tbody').select('input', 'select'); for(var i=0; i { + elm.setAttribute('draggable', true); + this._wrap(elm); + elm.addEventListener('dragstart', this.handlerOnDragStart, false); + elm.addEventListener('dragenter', this.handlerOnDragEnter, false); + elm.addEventListener('dragover', this.handlerOnDragOver, false); + elm.addEventListener('drop', this.handlerOnDrop, false); + elm.addEventListener('dragend', this.handlerOnDragEnd, false); + }); + this._getResetBtn().addEventListener('click', this.handlerResetColumnsOrder, false); + this._getResetBtn().classList.remove('no-display'); + + this._getToggleBtn().removeEventListener('click', this.handlerEnableColumnsOrder, false); + this._getToggleBtn().addEventListener('click', this.handlerDisableColumnsOrder, false); + + this.enabled = true; + } + + disableColumnsRearrangement() { + this.getColumns().forEach((elm) => { + elm.removeAttribute('draggable'); + this._unwrap(elm); + elm.removeEventListener('dragstart', this.handlerOnDragStart, false); + elm.removeEventListener('dragenter', this.handlerOnDragEnter, false); + elm.removeEventListener('dragover', this.handlerOnDragOver, false); + elm.removeEventListener('drop', this.handlerOnDrop, false); + elm.removeEventListener('dragend', this.handlerOnDragEnd, false); + }); + this._getResetBtn().removeEventListener('click', this.handlerResetColumnsOrder, false); + this._getResetBtn().classList.add('no-display'); + + this._getToggleBtn().removeEventListener('click', this.handlerDisableColumnsOrder, false); + this._getToggleBtn().addEventListener('click', this.handlerEnableColumnsOrder, false); + + this.enabled = false; + } + + buildDragPreview(elm) { + const _columnIndex = elm.cellIndex + 1; + + const _tableColPReview = document.createElement('table'); + _tableColPReview.style.width = elm.getBoundingClientRect().width + 'px'; + + const _tHead = _tableColPReview.createTHead(); + + const trHeading = _tHead.insertRow(); + trHeading.classList.add('headings'); + trHeading.append(elm.clone(true)); + + const trFilter = _tHead.insertRow(); + trFilter.classList.add('filter'); + const _selectorFilterRows = 'thead tr.filter th:nth-child(' + _columnIndex + ')'; + Array.from(elm.closest('table').querySelectorAll( _selectorFilterRows )).forEach((row) => { + trFilter.appendChild(row.clone(true)); + }); + + const _tBody = _tableColPReview.createTBody(); + const _selectorRows = 'tbody td:nth-child(' + _columnIndex + ')'; + Array.from(elm.closest('table').querySelectorAll( _selectorRows )).forEach((row) => { + const tr = _tBody.insertRow(); + tr.appendChild(row.clone(true)); + }); + + const previewGrid = document.createElement('div'); + previewGrid.classList.add('grid'); + previewGrid.classList.add('grid__dragPreview'); + previewGrid.style.width = elm.getBoundingClientRect().width + 'px'; + previewGrid.setAttribute('id', 'dragPreview_placeholder'); + previewGrid.append(_tableColPReview); + + return previewGrid; + } + + onDragStart(e) { + const placeholder = this.buildDragPreview(e.target.closest(this._columnSelector)); + document.body.appendChild(placeholder); + e.dataTransfer.setDragImage(placeholder, 0, 0); + + e.dataTransfer.effectAllowed = "copyMove"; + e.dataTransfer.setData("text/plain", null); + this._currentDragItem = e.target.closest(this._columnSelector); + } + + onDragOver(e) { + if (e.preventDefault) { + e.preventDefault(); + } + + if (e.stopPropagation) { + e.stopPropagation(); + } + } + + onDragEnter(e) { + this._resetClassStyle(); + + // disable drop self + let _currentTargetColumn = e.target.closest(this._columnSelector); + if (this._isSameColumn(_currentTargetColumn)) { + this._targetDragColumn = null; + return false; + } + + this._targetDragColumn = _currentTargetColumn; + + this._targetDragColumn.classList.add('overDrag'); + if (this._checkElementisBefore(this._currentDragItem, this._targetDragColumn)) { + this._targetDragColumn.classList.add('overDrag__left'); + } else { + this._targetDragColumn.classList.add('overDrag__right'); + } + } + + onDrop(e) { + if (e.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation) { + e.stopPropagation(); + } + + if (this._targetDragColumn) { + if (this._checkElementisBefore(this._currentDragItem, this._targetDragColumn)) { + e.target.parentNode.closest('tr').insertBefore(this._currentDragItem, this._targetDragColumn); + } else { + e.target.parentNode.closest('tr').insertBefore(this._currentDragItem, this._targetDragColumn.nextSibling); + } + this._saveCurrentColumnsOrder(true); + } + return false; + } + + resetColumnsOrder() { + if (confirm(this.resetConfirmText)) { + new Ajax.Request(this.url + (this.url.match(new RegExp('\\?')) ? '&ajax=true' : '?ajax=true' ), { + method: 'post', + dataType: "json", + parameters: { + "gridId": this.containerId, + "reset": true + }, + onComplete: this.onReorderComplete.bind(this), + onSuccess: function(transport) {} + }, this); + } + } + + onDragEnd(e) { + this._resetClassStyle(); + + document.getElementById('dragPreview_placeholder').remove(); + + if (this.columnOrderChanged) { + new Ajax.Request(this.url + (this.url.match(new RegExp('\\?')) ? '&ajax=true' : '?ajax=true' ), { + method: 'post', + dataType: "json", + parameters: { + "gridId": this.containerId, + "data": JSON.stringify(this._currentColumnsOrder) + }, + onComplete: this.onReorderComplete.bind(this), + onSuccess: function(transport) {} + }, this); + } + } + + onReorderComplete(transport) { + this.columnOrderChanged = false; + this.grid.reload(); + } + + // ensure varienGrid events propagation + getOldCallback(callbackName) { + return this.oldCallbacks[callbackName] ? this.oldCallbacks[callbackName] : Prototype.emptyFunction; + } + + setOldCallback(callbackName, callback) { + this.oldCallbacks[callbackName] = callback; + } + + onGridInit(grid) { + this.initColumnsRearrangement(); + this.getOldCallback('init')(grid); + } + + getColumns() { + return Array.from(this._getTableHTMLElement().querySelectorAll(this._columnSelector)); + } + + _getContainerHTMLElement() { + return document.getElementById(this.containerId); + } + + _getTableHTMLElement() { + return this._getContainerHTMLElement().querySelector('#' + this.containerId + '_table'); + } + + _getToggleBtn() { + return this._getContainerHTMLElement().querySelector(this._btnToggleSelector); + } + + _getResetBtn() { + return this._getContainerHTMLElement().querySelector(this._btnResetSelector); + } + + _saveCurrentColumnsOrder(orderChanged = false) { + this._currentColumnsOrder = []; + this.getColumns().forEach((elm) => { + this._currentColumnsOrder.push(elm.getAttribute('data-entity')); + }); + if (orderChanged != false) { + this.columnOrderChanged = true; + } + return this._currentColumnsOrder; + } + + _checkElementisBefore(el1, el2) { + if (el2.parentNode === el1.parentNode) { + for (var cur = el1.previousSibling; cur && cur.nodeType !== 9; cur = cur.previousSibling) { + if (cur === el2) { + return true; + } + } + } + return false; + } + + _isSameColumn(element) { + if (this._currentDragItem == element) { + return true; + } + return false; + } + + _resetClassStyle() { + this.getColumns().forEach((elm) => { + elm.classList.remove('overDrag'); + elm.classList.remove('overDrag__left'); + elm.classList.remove('overDrag__right'); + }); + } + + _wrap(target) { + if (!target.querySelector('.draggable')) { + let wrapper = document.createElement('span'); + wrapper.classList.add('draggable'); + [ ...target.childNodes ].forEach(child => wrapper.appendChild(child)); + target.appendChild(wrapper); + return wrapper; + } + } + + _unwrap(target) { + let wrapper = target.querySelector('.draggable'); + if (wrapper) { + target.querySelector('.draggable').replaceWith(...target.querySelector('.draggable').childNodes); + } + } +} \ No newline at end of file diff --git a/skin/adminhtml/default/default/boxes.css b/skin/adminhtml/default/default/boxes.css index a73854578cc..ce4c7ab083e 100644 --- a/skin/adminhtml/default/default/boxes.css +++ b/skin/adminhtml/default/default/boxes.css @@ -186,7 +186,6 @@ table.actions td { vertical-align:top; } .massaction a { text-decoration:none; } .massaction .entry-edit fieldset span.form_row, .massaction .entry-edit fieldset span.field-row { clear:none !important; display:inline; float:left !important; margin:0; padding:0 5px 0 0; } - .massaction .entry-edit .outer-span { float:left; } /* Grid - Filter */ @@ -220,6 +219,23 @@ tr.dynamic-grid input.input-text { width:154px; } .available { color:#080; font-weight:bold; } .not-available { color:#800; } + +/* Grid heading drag&drop */ +.overDrag { background-color: rgba(255, 255, 255, 0.60) !important; } +.overDrag:after { content: ''; position: absolute; background: #a92000; top: 0; width: 4px; height: 100% } +.overDrag__left::after { left:0; } +.overDrag__right::after { right: 0; } +.grid__dragPreview { border: 1px solid #a92000; } +.grid tr.headings th[draggable], +.grid tr.headings th[draggable] a { cursor: move; position: relative; } +.grid tr.headings th[draggable] span.draggable { display: flex; gap: 8px; align-items: center; } +.grid tr.headings th[draggable] span.draggable > * { flex-grow: 1; } +.grid tr.headings th[draggable] span.draggable:before { + content: url('data:image/svg+xml,'); + height: 14px; + aspect-ratio: 1; +} + /* Grid - Copyable Cells */ .icon-copy { display: inline-block; @@ -236,6 +252,7 @@ tr.dynamic-grid input.input-text { width:154px; } .icon-copy-copied { background-image: url('data:image/svg+xml,'); } + /* ACCORDION *******************************************************************************/ dl.accordion .grid { margin-bottom:0; }