Skip to content

Commit

Permalink
Merge pull request #7 from ericthehacker/feature/scope-hint-values-di…
Browse files Browse the repository at this point in the history
…splay

Show override value hint
  • Loading branch information
ericthehacker authored Dec 31, 2016
2 parents 83f8471 + d068220 commit 80bd8da
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 30 deletions.
138 changes: 113 additions & 25 deletions Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
const STORE_VIEW_SCOPE_CODE = 'stores';
const WEBSITE_SCOPE_CODE = 'websites';

/** @var \Magento\Framework\App\Helper\Context */
protected $context;

Expand All @@ -18,16 +21,29 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper
* @var \Magento\Backend\Model\Url
*/
protected $urlBuilder;
/**
* @var \Magento\Config\Model\Config\Structure\SearchInterface
*/
protected $configStructure;
/**
* @var \Magento\Framework\Escaper
*/
protected $escaper;

/**
* Data constructor.
* @param \Magento\Framework\App\Helper\Context $context
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Backend\Model\Url $urlBuilder
* @param \Magento\Config\Model\Config\Structure\SearchInterface $configStructure
* @param \Magento\Framework\Escaper $escaper
*/
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Backend\Model\Url $urlBuilder
\Magento\Backend\Model\Url $urlBuilder,
\Magento\Config\Model\Config\Structure\SearchInterface $configStructure,
\Magento\Framework\Escaper $escaper
) {
parent::__construct($context);

Expand All @@ -37,6 +53,8 @@ public function __construct(
// an instance of \Magento\Framework\Url instead of \Magento\Backend\Model\Url, we must explicitly request it
// via DI.
$this->urlBuilder = $urlBuilder;
$this->configStructure = $configStructure;
$this->escaper = $escaper;
}

/**
Expand All @@ -46,17 +64,17 @@ public function __construct(
* @return array
*/
public function getScopeTree() {
$tree = array('websites' => array());
$tree = array(self::WEBSITE_SCOPE_CODE => array());

$websites = $this->storeManager->getWebsites();

/* @var $website Website */
foreach($websites as $website) {
$tree['websites'][$website->getId()] = array('stores' => array());
$tree[self::WEBSITE_SCOPE_CODE][$website->getId()] = array(self::STORE_VIEW_SCOPE_CODE => array());

/* @var $store Store */
foreach($website->getStores() as $store) {
$tree['websites'][$website->getId()]['stores'][] = $store->getId();
$tree[self::WEBSITE_SCOPE_CODE][$website->getId()][self::STORE_VIEW_SCOPE_CODE][] = $store->getId();
}
}

Expand All @@ -66,15 +84,51 @@ public function getScopeTree() {
/**
* Wrapper method to get config value at path, scope, and scope code provided
*
* @param $path
* @param $contextScope
* @param $contextScopeId
* @return mixed
* @param string $path
* @param string $contextScope
* @param string|int $contextScopeId
* @return string
*/
protected function _getConfigValue($path, $contextScope, $contextScopeId) {
return $this->context->getScopeConfig()->getValue($path, $contextScope, $contextScopeId);
}

/**
* Gets human-friendly display value(s) for given config path
*
* @param string $path
* @param string $contextScope
* @param string|int $contextScopeId
* @return array
*/
public function getConfigDisplayValue($path, $contextScope, $contextScopeId) {
$value = $this->_getConfigValue($path, $contextScope, $contextScopeId);

$labels = [$value]; //default labels to raw value

/** @var \Magento\Config\Model\Config\Structure\Element\Field $field */
$field = $this->configStructure->getElement($path);

if($field->getOptions()) {
$labels = []; //reset labels so we can add human-friendly labels

$optionsByValue = [];
foreach($field->getOptions() as $option) {
$optionsByValue[$option['value']] = $option;
}

$values = explode(',', $value);

foreach($values as $valueInstance) {
$labels[] = isset($optionsByValue[$valueInstance])
? $optionsByValue[$valueInstance]['label'] : $valueInstance;

}
}

return $labels;
}

/**
* Gets array of scopes and scope IDs where path value is different
* than supplied context scope and context scope ID.
Expand All @@ -90,41 +144,43 @@ public function getOverriddenLevels($path, $contextScope, $contextScopeId) {

$currentValue = $this->_getConfigValue($path, $contextScope, $contextScopeId);

if(is_null($currentValue)) {
return array(); //something is off, let's bail gracefully.
}

$overridden = array();

switch($contextScope) {
case 'websites':
$stores = array_values($tree['websites'][$contextScopeId]['stores']);
case self::WEBSITE_SCOPE_CODE:
$stores = array_values($tree[self::WEBSITE_SCOPE_CODE][$contextScopeId][self::STORE_VIEW_SCOPE_CODE]);
foreach($stores as $storeId) {
$value = $this->_getConfigValue($path, 'stores', $storeId);
$value = $this->_getConfigValue($path, self::STORE_VIEW_SCOPE_CODE, $storeId);
if($value != $currentValue) {
$overridden[] = array(
'scope' => 'store',
'scope_id' => $storeId
'scope_id' => $storeId,
'value' => $value,
'display_value' => $this->getConfigDisplayValue($path, self::STORE_VIEW_SCOPE_CODE, $storeId)
);
}
}
break;
case 'default':
foreach($tree['websites'] as $websiteId => $website) {
$websiteValue = $this->_getConfigValue($path, 'websites', $websiteId);
foreach($tree[self::WEBSITE_SCOPE_CODE] as $websiteId => $website) {
$websiteValue = $this->_getConfigValue($path, self::WEBSITE_SCOPE_CODE, $websiteId);
if($websiteValue != $currentValue) {
$overridden[] = array(
'scope' => 'website',
'scope_id' => $websiteId
'scope_id' => $websiteId,
'value' => $websiteValue,
'display_value' => $this->getConfigDisplayValue($path, self::WEBSITE_SCOPE_CODE, $websiteId)
);
}

foreach($website['stores'] as $storeId) {
$value = $this->_getConfigValue($path, 'stores', $storeId);
foreach($website[self::STORE_VIEW_SCOPE_CODE] as $storeId) {
$value = $this->_getConfigValue($path, self::STORE_VIEW_SCOPE_CODE, $storeId);
if($value != $currentValue && $value != $websiteValue) {
$overridden[] = array(
'scope' => 'store',
'scope_id' => $storeId
'scope_id' => $storeId,
'value' => $value,
'display_value' => $this->getConfigDisplayValue($path, self::STORE_VIEW_SCOPE_CODE, $storeId)
);
}
}
Expand All @@ -135,6 +191,31 @@ public function getOverriddenLevels($path, $contextScope, $contextScopeId) {
return $overridden;
}

/**
* Get HTML formatted value label(s)
*
* @param array $labels
* @return string
*/
protected function getFormattedValueLabels(array $labels) {
if(count($labels) == 1) {
//if only one value, simply return it
return '<span class="override-value-hint-label">' .
nl2br($this->escaper->escapeHtml($labels[0])) .
'</span>';
}

$formattedLabels = '';

foreach($labels as $label) {
$formattedLabels .= '<li class="override-value-hint-label">' .
nl2br($this->escaper->escapeHtml($label)) .
'</li>';
}

return '<ul class="override-value-hint-labels">' . $formattedLabels . '</ul>';
}

/**
* Get HTML output for override hint UI
*
Expand All @@ -145,11 +226,13 @@ public function getOverriddenLevels($path, $contextScope, $contextScopeId) {
public function formatOverriddenScopes($section, array $overridden) {
$formatted = '<div class="overridden-hint-wrapper">' .
'<p class="lead-text">' . __('This config field is overridden at the following scope(s):') . '</p>' .
'<ul class="overridden-hint-list">';
'<dl class="overridden-hint-list">';

foreach($overridden as $overriddenScope) {
$scope = $overriddenScope['scope'];
$scopeId = $overriddenScope['scope_id'];
$value = $overriddenScope['value'];
$valueLabel = $overriddenScope['display_value'];
$scopeLabel = $scopeId;

$url = '#';
Expand All @@ -170,6 +253,7 @@ public function formatOverriddenScopes($section, array $overridden) {

break;
case 'store':
/** @var \Magento\Store\Model\Store $store */
$store = $this->storeManager->getStore($scopeId);
$website = $store->getWebsite();
$url = $this->urlBuilder->getUrl(
Expand All @@ -187,10 +271,14 @@ public function formatOverriddenScopes($section, array $overridden) {
break;
}

$formatted .= '<li class="' . $scope . '">'. $scopeLabel .'</li>';
$formatted .=
'<dt class="override-scope ' . $scope . '" title="'. __('Click to see overridden value') .'">'
. $scopeLabel .
'</dt>' .
'<dd class="override-value">' . $this->getFormattedValueLabels($valueLabel) . '</dd>';
}

$formatted .= '</ul></div>';
$formatted .= '</dl></div>';

return $formatted;
}
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ After installing the module, when viewing a system configuration field, an alert

The icon is only shown when the value is overridden at a more specific scope than the current one – that is, if viewing at the default scope, overrides at the website or store view level are shown, but if viewing at the website level, only overrides below the currently selected website are shown.

Along with the alert message, a detailed list of the exact scope(s) that override the value, with links directly to the store config for the current section at those scopes.
Along with the alert message, a detailed list of the exact scope(s) that override the value, with links directly to the store config for the current section at those scopes. Clicking an override hint row arrow will expand the row to also show the field's value at that scope.

![Screenshot of system config scope hints module](https://ericisaweso.me/images/magento2-configscopehints-v3.png)
![Screenshot of system config scope hints module](https://ericisaweso.me/images/magento2-configscopehints-v3.1.png)

## Compatibility and Technical Notes

Expand Down
2 changes: 1 addition & 1 deletion etc/module.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
<module name="EW_ConfigScopeHints" setup_version="3.0.0">
<module name="EW_ConfigScopeHints" setup_version="3.1.0">
<sequence>
<module name="Magento_Config"/>
</sequence>
Expand Down
1 change: 1 addition & 0 deletions view/adminhtml/layout/adminhtml_system_config_edit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
<head>
<css src="EW_ConfigScopeHints::css/configscopehints.css"/>
<script src="EW_ConfigScopeHints::js/configscopehints.js"/>
</head>
</page>
39 changes: 37 additions & 2 deletions view/adminhtml/web/css/configscopehints.less
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,42 @@
}
}

.overridden-hint-list {
list-style-position: inside;
.override-scope {
cursor: pointer;
line-height: 40px; //improve touch experience
position: relative;
padding-right: 4rem; //prevent arrow from overlapping text

&:before {
font-family: "Admin Icons";
font-style: normal;
content: '\e616';
font-size: 1.8rem;
position: absolute;
right: 1.3rem;
}

&.open {
border-bottom: none; //remove native accordion border so following .override-value can have it

&:before {
content: '\e615'; //set icon to close arrow
}
}
}

.override-value {
&.visible {
display: block;
}

span.override-value-hint-label {
display: block;
margin-bottom: 1rem;
}

//move native accordion left whitespace from margin to padding
margin-left: 0;
padding-left: 40px;
}
}
9 changes: 9 additions & 0 deletions view/adminhtml/web/js/configscopehints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require([
'jquery'
], function ($) {
$(document).ready(function() {
$('.overridden-hint-list').on('click', '.override-scope', function() {
$(this).toggleClass('open').next('.override-value').toggleClass('visible');
});
});
});

0 comments on commit 80bd8da

Please sign in to comment.