Initial commit,修正

This commit is contained in:
2025-07-13 19:37:38 +08:00
parent 38ce288e27
commit 65bbd06981
12384 changed files with 3985464 additions and 8 deletions
+137
View File
@@ -0,0 +1,137 @@
<?php
namespace mdm\admin\components;
use yii\web\ForbiddenHttpException;
use yii\base\Module;
use Yii;
use yii\web\User;
use yii\di\Instance;
/**
* Access Control Filter (ACF) is a simple authorization method that is best used by applications that only need some simple access control.
* As its name indicates, ACF is an action filter that can be attached to a controller or a module as a behavior.
* ACF will check a set of access rules to make sure the current user can access the requested action.
*
* To use AccessControl, declare it in the application config as behavior.
* For example.
*
* ```
* 'as access' => [
* 'class' => 'mdm\admin\components\AccessControl',
* 'allowActions' => ['site/login', 'site/error']
* ]
* ```
*
* @property User $user
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 1.0
*/
class AccessControl extends \yii\base\ActionFilter
{
/**
* @var User User for check access.
*/
private $_user = 'user';
/**
* @var array List of action that not need to check access.
*/
public $allowActions = [];
/**
* Get user
* @return User
*/
public function getUser()
{
if (!$this->_user instanceof User) {
$this->_user = Instance::ensure($this->_user, User::className());
}
return $this->_user;
}
/**
* Set user
* @param User|string $user
*/
public function setUser($user)
{
$this->_user = $user;
}
/**
* @inheritdoc
*/
public function beforeAction($action)
{
$actionId = $action->getUniqueId();
$user = $this->getUser();
if (Helper::checkRoute('/' . $actionId, Yii::$app->getRequest()->get(), $user)) {
return true;
}
$this->denyAccess($user);
}
/**
* Denies the access of the user.
* The default implementation will redirect the user to the login page if he is a guest;
* if the user is already logged, a 403 HTTP exception will be thrown.
* @param User $user the current user
* @throws ForbiddenHttpException if the user is already logged in.
*/
protected function denyAccess($user)
{
if ($user->getIsGuest()) {
$user->loginRequired();
} else {
throw new ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
}
}
/**
* @inheritdoc
*/
protected function isActive($action)
{
$uniqueId = $action->getUniqueId();
if ($uniqueId === Yii::$app->getErrorHandler()->errorAction) {
return false;
}
$user = $this->getUser();
if ($user->getIsGuest() && is_array($user->loginUrl) && isset($user->loginUrl[0]) && $uniqueId === trim($user->loginUrl[0], '/')) {
return false;
}
if ($this->owner instanceof Module) {
// convert action uniqueId into an ID relative to the module
$mid = $this->owner->getUniqueId();
$id = $uniqueId;
if ($mid !== '' && strpos($id, $mid . '/') === 0) {
$id = substr($id, strlen($mid) + 1);
}
} else {
$id = $action->id;
}
foreach ($this->allowActions as $route) {
if (substr($route, -1) === '*') {
$route = rtrim($route, "*");
if ($route === '' || strpos($id, $route) === 0) {
return false;
}
} else {
if ($id === $route) {
return false;
}
}
}
if ($action->controller->hasMethod('allowAction') && in_array($action->id, $action->controller->allowAction())) {
return false;
}
return true;
}
}
+202
View File
@@ -0,0 +1,202 @@
<?php
namespace mdm\admin\components;
use Yii;
use yii\db\Connection;
use yii\caching\Cache;
use yii\helpers\ArrayHelper;
use yii\di\Instance;
/**
* Configs
* Used for configure some value. To set config you can use [[\yii\base\Application::$params]]
*
* ```
* return [
*
* 'mdm.admin.configs' => [
* 'db' => 'customDb',
* 'menuTable' => '{{%admin_menu}}',
* 'cache' => [
* 'class' => 'yii\caching\DbCache',
* 'db' => ['dsn' => 'sqlite:@runtime/admin-cache.db'],
* ],
* ]
* ];
* ```
*
* or use [[\Yii::$container]]
*
* ```
* Yii::$container->set('mdm\admin\components\Configs',[
* 'db' => 'customDb',
* 'menuTable' => 'admin_menu',
* ]);
* ```
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 1.0
*/
// class Configs extends \yii\base\Object //更新使用7.2以上版本PHP后,不再可使用Object类 By fm453
class Configs extends \yii\base\BaseObject
{
const CACHE_TAG = 'mdm.admin';
/**
* @var Connection Database connection.
*/
public $db = 'db';
/**
* @var Cache Cache component.
*/
public $cache = 'cache';
/**
* @var integer Cache duration. Default to a hour.
*/
public $cacheDuration = 3600;
/**
* @var string Menu table name.
*/
public $menuTable = '{{%c_menu}}';
/**
* @var string Menu table name.
*/
//public $userTable = '{{%user}}';
public $userTable = '{{%c_adminer}}';
/**
* @var integer Default status user signup. 10 mean active.
*/
public $defaultUserStatus = 10;
/**
* @var boolean If true then AccessControl only check if route are registered.
*/
public $onlyRegisteredRoute = false;
/**
* @var boolean If false then AccessControl will check without Rule.
*/
public $strict = true;
/**
* @var array
*/
public $options;
/**
* @var self Instance of self
*/
private static $_instance;
private static $_classes = [
'db' => 'yii\db\Connection',
'cache' => 'yii\caching\Cache',
];
/**
* @inheritdoc
*/
public function init()
{
foreach (self::$_classes as $key => $class) {
try {
$this->{$key} = empty($this->{$key}) ? null : Instance::ensure($this->{$key}, $class);
} catch (\Exception $exc) {
$this->{$key} = null;
Yii::error($exc->getMessage());
}
}
}
/**
* Create instance of self
* @return static
*/
public static function instance()
{
if (self::$_instance === null) {
$type = ArrayHelper::getValue(Yii::$app->params, 'mdm.admin.configs', []);
if (is_array($type) && !isset($type['class'])) {
$type['class'] = static::className();
}
return self::$_instance = Yii::createObject($type);
}
return self::$_instance;
}
public static function __callStatic($name, $arguments)
{
$instance = static::instance();
if ($instance->hasProperty($name)) {
return $instance->$name;
} else {
if (count($arguments)) {
$instance->options[$name] = reset($arguments);
} else {
return array_key_exists($name, $instance->options) ? $instance->options[$name] : null;
}
}
}
/**
* @return Connection
*/
public static function db()
{
return static::instance()->db;
}
/**
* @return Cache
*/
public static function cache()
{
return static::instance()->cache;
}
/**
* @return integer
*/
public static function cacheDuration()
{
return static::instance()->cacheDuration;
}
/**
* @return string
*/
public static function menuTable()
{
return static::instance()->menuTable;
}
/**
* @return string
*/
public static function userTable()
{
return static::instance()->userTable;
}
/**
* @return string
*/
public static function defaultUserStatus()
{
return static::instance()->defaultUserStatus;
}
/**
* @return boolean
*/
public static function onlyRegisteredRoute()
{
return static::instance()->onlyRegisteredRoute;
}
/**
* @return boolean
*/
public static function strict()
{
return static::instance()->strict;
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace mdm\admin\components;
/**
* DbManager represents an authorization manager that stores authorization information in database.
*
* The database connection is specified by [[$db]]. The database schema could be initialized by applying migration:
*
* ```
* yii migrate --migrationPath=@yii/rbac/migrations/
* ```
*
* If you don't want to use migration and need SQL instead, files for all databases are in migrations directory.
*
* You may change the names of the three tables used to store the authorization data by setting [[\yii\rbac\DbManager::$itemTable]],
* [[\yii\rbac\DbManager::$itemChildTable]] and [[\yii\rbac\DbManager::$assignmentTable]].
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 1.0
*/
class DbManager extends \yii\rbac\DbManager
{
/**
* Memory cache of assignments
* @var array
*/
private $_assignments = [];
private $_childrenList;
/**
* @inheritdoc
*/
public function getAssignments($userId)
{
if (!isset($this->_assignments[$userId])) {
$this->_assignments[$userId] = parent::getAssignments($userId);
}
return $this->_assignments[$userId];
}
/**
* @inheritdoc
*/
protected function getChildrenList()
{
if ($this->_childrenList === null) {
$this->_childrenList = parent::getChildrenList();
}
return $this->_childrenList;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace mdm\admin\components;
use yii\rbac\Rule;
/**
* Description of GuestRule
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 2.5
*/
class GuestRule extends Rule
{
/**
* @inheritdoc
*/
public $name = 'guest_rule';
/**
* @inheritdoc
*/
public function execute($user, $item, $params)
{
return $user->getIsGuest();
}
}
+237
View File
@@ -0,0 +1,237 @@
<?php
namespace mdm\admin\components;
use Yii;
use yii\web\User;
use yii\helpers\ArrayHelper;
use yii\caching\TagDependency;
/**
* Description of Helper
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 2.3
*/
class Helper
{
private static $_userRoutes = [];
private static $_defaultRoutes;
private static $_routes;
public static function getRegisteredRoutes()
{
if (self::$_routes === null) {
self::$_routes = [];
$manager = Yii::$app->getAuthManager();
foreach ($manager->getPermissions() as $item) {
if ($item->name[0] === '/') {
self::$_routes[$item->name] = $item->name;
}
}
}
return self::$_routes;
}
/**
* Get assigned routes by default roles
* @return array
*/
protected static function getDefaultRoutes()
{
if (self::$_defaultRoutes === null) {
$manager = Yii::$app->getAuthManager();
$roles = $manager->defaultRoles;
$cache = Configs::cache();
if ($cache && ($routes = $cache->get($roles)) !== false) {
self::$_defaultRoutes = $routes;
} else {
$permissions = self::$_defaultRoutes = [];
foreach ($roles as $role) {
$permissions = array_merge($permissions, $manager->getPermissionsByRole($role));
}
foreach ($permissions as $item) {
if ($item->name[0] === '/') {
self::$_defaultRoutes[$item->name] = true;
}
}
if ($cache) {
$cache->set($roles, self::$_defaultRoutes, Configs::cacheDuration(), new TagDependency([
'tags' => Configs::CACHE_TAG
]));
}
}
}
return self::$_defaultRoutes;
}
/**
* Get assigned routes of user.
* @param integer $userId
* @return array
*/
public static function getRoutesByUser($userId)
{
if (!isset(self::$_userRoutes[$userId])) {
$cache = Configs::cache();
if ($cache && ($routes = $cache->get([__METHOD__, $userId])) !== false) {
self::$_userRoutes[$userId] = $routes;
} else {
$routes = static::getDefaultRoutes();
$manager = Yii::$app->getAuthManager();
foreach ($manager->getPermissionsByUser($userId) as $item) {
if ($item->name[0] === '/') {
$routes[$item->name] = true;
}
}
self::$_userRoutes[$userId] = $routes;
if ($cache) {
$cache->set([__METHOD__, $userId], $routes, Configs::cacheDuration(), new TagDependency([
'tags' => Configs::CACHE_TAG
]));
}
}
}
return self::$_userRoutes[$userId];
}
/**
* Check access route for user.
* @param string|array $route
* @param integer|User $user
* @return boolean
*/
public static function checkRoute($route, $params = [], $user = null)
{
$config = Configs::instance();
$r = static::normalizeRoute($route);
if ($config->onlyRegisteredRoute && !isset(static::getRegisteredRoutes()[$r])) {
return true;
}
if ($user === null) {
$user = Yii::$app->getUser();
}
$userId = $user instanceof User ? $user->getId() : $user;
if ($config->strict) {
if ($user->can($r, $params)) {
return true;
}
while (($pos = strrpos($r, '/')) > 0) {
$r = substr($r, 0, $pos);
if ($user->can($r . '/*', $params)) {
return true;
}
}
return $user->can('/*', $params);
} else {
$routes = static::getRoutesByUser($userId);
if (isset($routes[$r])) {
return true;
}
while (($pos = strrpos($r, '/')) > 0) {
$r = substr($r, 0, $pos);
if (isset($routes[$r . '/*'])) {
return true;
}
}
return isset($routes['/*']);
}
}
protected static function normalizeRoute($route)
{
if ($route === '') {
return '/' . Yii::$app->controller->getRoute();
} elseif (strncmp($route, '/', 1) === 0) {
return $route;
} elseif (strpos($route, '/') === false) {
return '/' . Yii::$app->controller->getUniqueId() . '/' . $route;
} elseif (($mid = Yii::$app->controller->module->getUniqueId()) !== '') {
return '/' . $mid . '/' . $route;
}
return '/' . $route;
}
/**
* Filter menu items
* @param array $items
* @param integer|User $user
*/
public static function filter($items, $user = null)
{
if ($user === null) {
$user = Yii::$app->getUser();
}
return static::filterRecursive($items, $user);
}
/**
* Filter menu recursive
* @param array $items
* @param integer|User $user
* @return array
*/
protected static function filterRecursive($items, $user)
{
$result = [];
foreach ($items as $i => $item) {
$url = ArrayHelper::getValue($item, 'url', '#');
$allow = is_array($url) ? static::checkRoute($url[0], array_slice($url, 1), $user) : true;
if (isset($item['items']) && is_array($item['items'])) {
$subItems = self::filterRecursive($item['items'], $user);
if (count($subItems)) {
$allow = true;
}
$item['items'] = $subItems;
}
if ($allow) {
$result[$i] = $item;
}
}
return $result;
}
/**
* Filter action column button. Use with [[yii\grid\GridView]]
* ```php
* 'columns' => [
* ...
* [
* 'class' => 'yii\grid\ActionColumn',
* 'template' => Helper::filterActionColumn(['view','update','activate'])
* ]
* ],
* ```
* @param array|string $buttons
* @param integer|User $user
* @return string
*/
public static function filterActionColumn($buttons = [], $user = null)
{
if (is_array($buttons)) {
$result = [];
foreach ($buttons as $button) {
if (static::checkRoute($button, [], $user)) {
$result[] = "{{$button}}";
}
}
return implode(' ', $result);
}
return preg_replace_callback('/\\{([\w\-\/]+)\\}/', function ($matches) use ($user) {
return static::checkRoute($matches[1], [], $user) ? "{{$matches[1]}}" : '';
}, $buttons);
}
/**
* Use to invalidate cache.
*/
public static function invalidate()
{
if (Configs::cache() !== null) {
TagDependency::invalidate(Configs::cache(), Configs::CACHE_TAG);
}
}
}
+190
View File
@@ -0,0 +1,190 @@
<?php
namespace mdm\admin\components;
use Yii;
use mdm\admin\models\AuthItem;
use mdm\admin\models\searchs\AuthItem as AuthItemSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\base\NotSupportedException;
use yii\filters\VerbFilter;
use yii\rbac\Item;
/**
* AuthItemController implements the CRUD actions for AuthItem model.
*
* @property integer $type
* @property array $labels
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 1.0
*/
class ItemController extends Controller
{
/**
* @inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
'assign' => ['post'],
'remove' => ['post'],
],
],
];
}
/**
* Lists all AuthItem models.
* @return mixed
*/
public function actionIndex()
{
$searchModel = new AuthItemSearch(['type' => $this->type]);
$dataProvider = $searchModel->search(Yii::$app->request->getQueryParams());
return $this->render('index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
/**
* Displays a single AuthItem model.
* @param string $id
* @return mixed
*/
public function actionView($id)
{
$model = $this->findModel($id);
return $this->render('view', ['model' => $model]);
}
/**
* Creates a new AuthItem model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionCreate()
{
$model = new AuthItem(null);
$model->type = $this->type;
if ($model->load(Yii::$app->getRequest()->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->name]);
} else {
return $this->render('create', ['model' => $model]);
}
}
/**
* Updates an existing AuthItem model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param string $id
* @return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->getRequest()->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->name]);
}
return $this->render('update', ['model' => $model]);
}
/**
* Deletes an existing AuthItem model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param string $id
* @return mixed
*/
public function actionDelete($id)
{
$model = $this->findModel($id);
Yii::$app->getAuthManager()->remove($model->item);
Helper::invalidate();
return $this->redirect(['index']);
}
/**
* Assign items
* @param string $id
* @return array
*/
public function actionAssign($id)
{
$items = Yii::$app->getRequest()->post('items', []);
$model = $this->findModel($id);
$success = $model->addChildren($items);
Yii::$app->getResponse()->format = 'json';
return array_merge($model->getItems(), ['success' => $success]);
}
/**
* Assign or remove items
* @param string $id
* @return array
*/
public function actionRemove($id)
{
$items = Yii::$app->getRequest()->post('items', []);
$model = $this->findModel($id);
$success = $model->removeChildren($items);
Yii::$app->getResponse()->format = 'json';
return array_merge($model->getItems(), ['success' => $success]);
}
/**
* @inheritdoc
*/
public function getViewPath()
{
return $this->module->getViewPath() . DIRECTORY_SEPARATOR . 'item';
}
/**
* Label use in view
* @throws NotSupportedException
*/
public function labels()
{
throw new NotSupportedException(get_class($this) . ' does not support labels().');
}
/**
* Type of Auth Item.
* @return integer
*/
public function getType()
{
}
/**
* Finds the AuthItem model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param string $id
* @return AuthItem the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
$auth = Yii::$app->getAuthManager();
$item = $this->type === Item::TYPE_ROLE ? $auth->getRole($id) : $auth->getPermission($id);
if ($item) {
return new AuthItem($item);
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
+222
View File
@@ -0,0 +1,222 @@
<?php
namespace mdm\admin\components;
use Yii;
use yii\caching\TagDependency;
use mdm\admin\models\Menu;
/**
* MenuHelper used to generate menu depend of user role.
* Usage
*
* ```
* use mdm\admin\components\MenuHelper;
* use yii\bootstrap\Nav;
*
* echo Nav::widget([
* 'items' => MenuHelper::getAssignedMenu(Yii::$app->user->id)
* ]);
* ```
*
* To reformat returned, provide callback to method.
*
* ```
* $callback = function ($menu) {
* $data = eval($menu['data']);
* return [
* 'label' => $menu['name'],
* 'url' => [$menu['route']],
* 'options' => $data,
* 'items' => $menu['children']
* ]
* ]
* }
*
* $items = MenuHelper::getAssignedMenu(Yii::$app->user->id, null, $callback);
* ```
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 1.0
*/
class MenuHelper
{
/**
* Use to get assigned menu of user.
* @param mixed $userId
* @param integer $root
* @param \Closure $callback use to reformat output.
* callback should have format like
*
* ```
* function ($menu) {
* return [
* 'label' => $menu['name'],
* 'url' => [$menu['route']],
* 'options' => $data,
* 'items' => $menu['children']
* ]
* ]
* }
* ```
* @param boolean $refresh
* @return array
*/
public static function getAssignedMenu($userId, $root = null, $callback = null, $refresh = false)
{
$config = Configs::instance();
/* @var $manager \yii\rbac\BaseManager */
$manager = Yii::$app->getAuthManager();
$menus = Menu::find()->asArray()->indexBy('id')->all();
$key = [__METHOD__, $userId, $manager->defaultRoles];
$cache = $config->cache;
if ($refresh || $cache === null || ($assigned = $cache->get($key)) === false) {
$routes = $filter1 = $filter2 = [];
if ($userId !== null) {
foreach ($manager->getPermissionsByUser($userId) as $name => $value) {
if ($name[0] === '/') {
if (substr($name, -2) === '/*') {
$name = substr($name, 0, -1);
}
$routes[] = $name;
}
}
}
foreach ($manager->defaultRoles as $role) {
foreach ($manager->getPermissionsByRole($role) as $name => $value) {
if ($name[0] === '/') {
if (substr($name, -2) === '/*') {
$name = substr($name, 0, -1);
}
$routes[] = $name;
}
}
}
$routes = array_unique($routes);
sort($routes);
$prefix = '\\';
foreach ($routes as $route) {
if (strpos($route, $prefix) !== 0) {
if (substr($route, -1) === '/') {
$prefix = $route;
$filter1[] = $route . '%';
} else {
$filter2[] = $route;
}
}
}
$assigned = [];
$query = Menu::find()->select(['id'])->asArray();
if (count($filter2)) {
$assigned = $query->where(['route' => $filter2])->column();
}
if (count($filter1)) {
$query->where('route like :filter');
foreach ($filter1 as $filter) {
$assigned = array_merge($assigned, $query->params([':filter' => $filter])->column());
}
}
$assigned = static::requiredParent($assigned, $menus);
if ($cache !== null) {
$cache->set($key, $assigned, $config->cacheDuration, new TagDependency([
'tags' => Configs::CACHE_TAG
]));
}
}
$key = [__METHOD__, $assigned, $root];
if ($refresh || $callback !== null || $cache === null || (($result = $cache->get($key)) === false)) {
$result = static::normalizeMenu($assigned, $menus, $callback, $root);
if ($cache !== null && $callback === null) {
$cache->set($key, $result, $config->cacheDuration, new TagDependency([
'tags' => Configs::CACHE_TAG
]));
}
}
return $result;
}
/**
* Ensure all item menu has parent.
* @param array $assigned
* @param array $menus
* @return array
*/
private static function requiredParent($assigned, &$menus)
{
$l = count($assigned);
for ($i = 0; $i < $l; $i++) {
$id = $assigned[$i];
$parent_id = $menus[$id]['parent'];
if ($parent_id !== null && !in_array($parent_id, $assigned)) {
$assigned[$l++] = $parent_id;
}
}
return $assigned;
}
/**
* Parse route
* @param string $route
* @return mixed
*/
public static function parseRoute($route)
{
if (!empty($route)) {
$url = [];
$r = explode('&', $route);
$url[0] = $r[0];
unset($r[0]);
foreach ($r as $part) {
$part = explode('=', $part);
$url[$part[0]] = isset($part[1]) ? $part[1] : '';
}
return $url;
}
return '#';
}
/**
* Normalize menu
* @param array $assigned
* @param array $menus
* @param Closure $callback
* @param integer $parent
* @return array
*/
private static function normalizeMenu(&$assigned, &$menus, $callback, $parent = null)
{
$result = [];
$order = [];
foreach ($assigned as $id) {
$menu = $menus[$id];
if ($menu['parent'] == $parent) {
$menu['children'] = static::normalizeMenu($assigned, $menus, $callback, $id);
if ($callback !== null) {
$item = call_user_func($callback, $menu);
} else {
$item = [
'label' => $menu['name'],
'url' => static::parseRoute($menu['route']),
];
if ($menu['children'] != []) {
$item['items'] = $menu['children'];
}
}
$result[] = $item;
$order[] = $menu['order'];
}
}
if ($result != []) {
array_multisort($order, $result);
}
return $result;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace mdm\admin\components;
use Yii;
use yii\rbac\Rule;
/**
* RouteRule Rule for check route with extra params.
*
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 1.0
*/
class RouteRule extends Rule
{
const RULE_NAME = 'route_rule';
/**
* @inheritdoc
*/
public $name = self::RULE_NAME;
/**
* @inheritdoc
*/
public function execute($user, $item, $params)
{
$routeParams = isset($item->data['params']) ? $item->data['params'] : [];
foreach ($routeParams as $key => $value) {
if (!array_key_exists($key, $params) || $params[$key] != $value) {
return false;
}
}
return true;
}
}