Using regex to make menu item active in laravel adminlte - php

2021: This was in fact an issue with the package. Regex link matching has since been implemented with the solution I created to solve it for myself. You can read about it in the documentation here.
I'm using Laravel 5.8 with AdminLTE for Laravel.
There's several options to create a menu, one of which is to create it in the provided config file, which I use.
You can specify an active key in the menu that allows you to make the menu have the class that makes the menu item active and activates the dropdown.
I have a menu item, which I would like to make active on these pages:
/posts (works with active => ['/posts'])
/posts/{post_id} (id are only numbers)
These URL's shouldn't match:
/posts/create
/posts/anyotherlink
/posts/1text
I can not use /posts/* because that would make the create page and some others active.
The readme suggest that you can also use regex to do this. I don't use regex at all, but I came to this, which, according tot regex101 seems to match what I need it to:
^\/posts\/[0-9]+$
I've tried to implement it like so:
[
'text' => 'Posts overview',
'url' => '/posts',
'icon' => 'list',
'active' => ['/posts', '^\/posts\/[0-9]+$'],
'active' => ['/posts', '/posts/[^0-9]'] // also tried this
],
Unfortunately, this does not seem to work as it doesn't make the menu item active on the pages listed above.
Edit: I've also created an issue in the GitHub repository as I suspect that this might be an issue with the package.
Am I missing something, or doing something wrong?

In the ActiveChecker.php class of the project you can find this piece of code
protected function checkPattern($pattern)
{
$fullUrlPattern = $this->url->to($pattern);
$fullUrl = $this->request->fullUrl();
return Str::is($fullUrlPattern, $fullUrl);
}
Based on laravel documentation, Str::is does not run regexp matching, but justs supports asterisks for wildcards.
In your case you could post a PR that will use regexo if the given pattern is a regular expression, otherwise, run Str::is

You can try using the below pattern in your code:
/^\/posts(\/[0-9]+)?$/
This will work for the below test scenarios:
/posts
/posts/3829
/posts/921
/posts/1
You can check the requirement with the provided solution on REGEX_Solution
Hope it works!

This is where route naming is really helpful. So, in my code this route would named something like admin:post.index. Then to check if it was active I could use the request helper and a method called routeIs().
Using the name I'd use, here's an example:
request()->routeIs('admin:post.index')
Now, if I wanted this link to be active for the create, edit, delete etc routes I'd do the following:
request()->routeIs('admin:post.*')
This would work since I apply route names following a dot notation hierarchy.
You can optionally provide multiple patterns to that method if there were specific routes you wanted to match.

I have created a solution for my problem, it's by no means the prettiest solution, but it'll work, for now.
I have edited the ActiveChecker.php file and edited the checkPattern() function, so it evaluates regex if the pattern in the config file starts with regex:.
protected function checkPattern($pattern)
{
$fullUrlPattern = $this->url->to($pattern);
$fullUrl = $this->request->fullUrl();
if(mb_substr($pattern, 0, 6) === "regex:") {
$regex = mb_substr($pattern, 6);
if(preg_match($regex, request()->path()) == 1) {
return true;
}
return false;
}
return Str::is($fullUrlPattern, $fullUrl);
}
In my config file, I can now just do this, to use a regex pattern, simply by using the regex: prefix.
'active' => ['/suppliers', 'regex:#^posts/[0-9]+$#'],
The path of the request which is posts/1 for example, will get compared with the regex, not the full URL.

Related

Router management in CI 3

I need router management in my project.
My url is some thing like it.
http://localhost/cloud/index.php/dashboard/view_tickets/wsyZCMuIEavPeWdRHqjJ
Here i want to replace "dashboard/view_tickets" as one word "tickets"
but i have tried using routers, its not working.
Router code:
$route['ticket/(:num)'] = 'Ticket/view_tickets/$1';
Here $1 is for num, but here my parameter has string only.
Any suggestion?
Thank you.
Change this:
$route['ticket/(:num)'] = 'Ticket/view_tickets/$1';
To this:
$route['ticket/(:any)'] = 'Ticket/view_tickets/$1';
To make it work, you should create a function called "view_tickets" under "Ticket" class (Controller).
Also please see here to understand routing in Codeigniter.

Laravel 4.1 - Advice on where to put some custom config stuff

I am working on a small application with Laravel 4.1. I created an app/acme folder to contain most of my app specific code. I have a service provider named app/acme/AcmeServiceProvider.php where I am setting IoC bindings, Custom View templates and some Config variables.
My Custom Service Provider
...
View::addLocation(app('path').'/OneTimeNote/Views');
/* #TODO - Maybe a better place to put this would be in the app/config folder? */
Config::set('EXTERNAL_SITE_URL', 'http://www.example.com/#/');
/* #TODO - Probably could find a better spot for this, possibly in a lang folder? */
Config::set('EMAIL_SUBJECT', 'The note you created has been read and destroyed.');
...
I think having my custom View location in my service provider makes sense but what about the two entries below it?
The first Config is setting a value I can retrieve from my Controller so that I can build an external URL.
The second Config is setting a value I can use for an e-mail subject I'm sending the user. I thought about just hardcoding this value, but I wanted to put configurable text like this in one place for easy customization. I'd also like to utilize multiple languages in the future, so having some sort of 'lang' directory with different languages could be an answer I suppose?
My question: Is there a better place to put these two config statements rather than my Service Provider? Maybe they shouldn't be added to the Config at all but somewhere else? need advice.
Thanks!
Lets try by creating app/config/acme.php
and return array there e.g.
return array(
'EXTERNAL_SITE_URL' => 'SITE_URL',
'EMAIL_SUBJECT' => 'SUBJECt_VALUE'
);
try to access like that config::get('acme.EXTERNAL_SITE_URL'). I hope it should work for you. Please share you experience ....
#Edited: I have tested that is working for me, so should be for you as well :)
#Edited: Language Part
You can add file in app/lang/en/acme.php (e.g. for english)
if there is another language you can add app/lang/it/acme.php (e.g. for italian)
return array(
"abc" => "This is text",
"def" => array(
"sub1" => "this is fist sub message",
"sub2" => "this is on sencond number",
)
);
then you can get like App::setLocale('en');
echo Lang::get('acme.abc'); //output: This is text
echo Lang::get('acme.def.sub1'); //output: this is fist sub message

Get all "pages" in YII?

I'm trying to create my own xml sitemap. Everything is done except for the part that I thought was going to be the easiest. How do you get a list of all the pages on the site? I have a bunch of views in a /site folder and a few others. Is there a way to explicitly request their URLs or perhaps via the controllers?
I do not want to make use of an extension
You can use reflection to iterate through all methods of all your controllers:
Yii::import('application.controllers.*');
$urls = array();
$directory = Yii::getPathOfAlias('application.controllers');
$iterator = new DirectoryIterator($directory);
foreach ($iterator as $fileinfo)
{
if ($fileinfo->isFile() and $fileinfo->getExtension() == 'php')
{
$className = substr($fileinfo->getFilename(), 0, -4); //strip extension
$class = new ReflectionClass($className);
foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
{
$methodName = $method->getName();
//only take methods that begin with 'action', but skip actions() method
if (strpos($methodName, 'action') === 0 and $methodName != 'actions')
{
$controller = lcfirst(substr($className, 0, strrpos($className, 'Controller')));
$action = lcfirst(substr($methodName, 6));
$urls[] = Yii::app()->createAbsoluteUrl("$controller/$action");
}
}
}
}
You need to know what content you want to include in your sitemap.xml, I don't really think you want to include ALL pages in your sitemap.xml, or do you really want to include something like site.com/article/edit/1 ?
That said, you may only want the result from the view action in your controllers. truth is, you need to know what you want to indexed.
Do not think in terms of controllers/actions/views, but rather think of the resources in your system that you want indexed, be them articles, or pages, they are all in your database or stored somehow, so you can list them, and they have a URI that identifies them, getting the URI is a matter of invoking a couple functions.
There are two possiblities -
Case 1:
You are running a static website then you can find all your HTML inside 1 folder - protected/views/site/pages
http://www.yiiframework.com/wiki/22/how-to-display-static-pages-in-yii/
Case 2:
Website is dynamic. Tasks such as generating and regenerating Sitemaps can be classified into background tasks.
Running background taks can be achieved by emulating the browser which is possible in linux using - WGET, GET or lynx commands
Or, You can create a CronController as a CConsoleCommand. How to use Commands in YII is shown in link below -
http://tariffstreet.com/yii/2012/04/implementing-cron-jobs-with-yii-and-cconsolecommand/
Sitemap is an XML which lists your site's URL. But it does more than that.
It helps you visualize the structure of a website , you may have
category
subcategories.
While making a useful extension, above points can be kept into consideration before design.
Frameworks like Wordpress provide way to generate categorical sitemap.
So the metadata for each page is stored from before and using that metadata it discovers and group pages.
Solution by Reflection suggested by #Pavle is good and should be the way to go.
Consider there may be partial views and you may or may not want to list them as separate links.
So how much effort you want to put into creating the extension is subject to some of these as well.
You may either ask user to list down all variables in config fie and go from there which is not bad or you have to group pages and list using some techniques like reflection and parsing pages and looking for regex.
For ex - Based on module names you can group them first and controllers inside a module can form sub-group.
One first approach could be to iterate over the view files, but then you have to take into account that in some cases, views are not page destinations, but page sections included in another pages by using CController::renderPartial() method. By exploring CController's Class Reference I came upon the CController::actions() method.
So, I have not found any Yii way to iterate over all the actions of a CController, but I used php to iterate over all the methods of a SiteController in one of my projects and filter them to these with the prefix 'action', which is my action prefix, here's the sample
class SiteController extends Controller{
public function actionTest(){
echo '<h1>Test Page!</h1></p>';
$methods = get_class_methods(get_class($this));
// The action prefix is strlen('action') = 6
$actionPrefix = 'action';
$reversedActionPrefix = strrev($actionPrefix);
$actionPrefixLength = strlen($actionPrefix);
foreach ($methods as $index=>$methodName){
//Always unset actions(), since it is not a controller action itself and it has the prefix 'action'
if ($methodName==='actions') {
unset($methods[$index]);
continue;
}
$reversedMethod = strrev($methodName);
/* if the last 6 characters of the reversed substring === 'noitca',
* it means that $method Name corresponds to a Controller Action,
* otherwise it is an inherited method and must be unset.
*/
if (substr($reversedMethod, -$actionPrefixLength)!==$reversedActionPrefix){
unset($methods[$index]);
} else $methods[$index] = strrev(str_replace($reversedActionPrefix, '', $reversedMethod,$replace=1));
}
echo 'Actions '.CHtml::listBox('methods', NULL, $methods);
}
...
}
And the output I got was..
I'm sure it can be furtherly refined, but this method should work for any of the controllers you have...
So what you have to do is:
For each Controller: Filter out all the not-action methods of the class, using the above method. You can build an associative array like
array(
'controllerName1'=>array(
'action1_1',
'action1_2'),
'controllerName2'=>array(
'action2_1',
'action2_2'),
);
I would add a static method getAllActions() in my SiteController for this.
get_class_methods, get_class, strrev and strlen are all PHP functions.
Based on your question:
1. How do you get a list of all the pages on the site?
Based on Yii's way of module/controller/action/action_params and your need to construct a sitemap for SEO.
It will be difficult to parse automatically to get all the urls as your action params varies indefinitely. Though you could simply get controller/action easily as constructed by
Pavle Predic. The complexity comes along when you have customized (SERF) URL rules meant for SEO.
The next best solution is to have a database of contents and you know how to get each content via url rules, then a cron console job to create all the urls to be saved as sitemap.xml.
Hope this helps!

Where to implement hook_search_info & _execute in order to force a language filter onto search results?

At the moment I am trying to force "current language" onto the list of the options passed into node_search_execute. Unfortunately I'm having trouble finding the right place to put the function hooks. Perhaps I am missing something simple.
I've got myself down to two basic possibilities for how this should be implemented.
(1) Implement hook_search_info and hook_search_execute
In this case, I'd copy the code from node_search_execute and add a line to it that adds "AND Language = '$current_language'" to the search query.
In my theme folder I've tried adding the functions mythemename_search_info and mythemename_search_execute - but they do not execute. When run.
function mythemename_search_info() {
return array(
'title' => 'Content',
'path' => 'node',
'conditions_callback' => 'mythemename_search_execute',
);
}
function mythemename_search_execute($keys = NULL, $conditions = NULL){
return array();
}
In this example - I'd just hope to get "no results" so I could be sure the override was running, then I'd implement the full search functionality.
(2) Implement hook_search_preprocess()
I also tried mythemename_search_preprocess()
function mythemename_search_preprocess($text) {
// Do processing on $text
echo $text; die();
$text = "french";
return $text;
}
But again, I don't get the expected results (a white page with a bit of text on it)
So whatever I'm doing, these search hooks are not getting detected.
What's missing? Do they perhaps have to be in a module?
Yes they do need to be in a module, most hooks are only called for modules and not themes. The most notable exception to this would be theme/preprocess hooks which are called for both.
In case you haven't made one before it's pretty straightforward to create a custom module, there's an invaluable guide here.
I used hook_search_info(), hook_search_execute() and hook_search_access() in my custom module. replaced "hook" with module name. I was able to get the tab created with 'title' of hook_search_info().
and passed he results array in hook_search_execute. with this the results started showing under the tab in search page. So definitely creating a new module will be of help to get a new search tab included.

Embedding CakePHP in existing page

I've volunteered to create some db app, and I told those guys that it will be very easy, since I wanted to use CakePHP. Sadly after some time they told me they want it inside their already existing web, which is ancient highly customized PHPNuke.
So what I want is to generate just content of one <div> inside an already existing page with CakePHP. I looked up on the internet, but I didn't find what I was looking for. I'm rather a user of the framework, not developer, so I don't know much about the backend and how MVC frameworks are working inside (and this is my first try with CakePHP, since I'm Rails guy).
What I did so far is disabling mod_rewrite for Cake. Inside PHPNuke module I included Cake's index.php and rendering views with an empty layout. This somehow works, but the thing is how to form URLs. I got it working by now with
http://localhost/modules.php/posts?op=modload&name=xxxxx&file=index&do=xxxxx
but with this all links to CSS and images on PHPNuke site are broken.
Is there any way to use something like
http://localhost/modules.php?op=modload&name=xxxxx&file=index&do=xxxxx&CakePHP=/posts/bla/bla
or any other way that could do the job? I really don't want to change anything in existing PHPNuke app.
Thank you very much
Well, if you don't understand how CakePHP works you'll have trouble doing what you want, since it would mean putting hacks into the CakePHP core files to bypass the default routing. This basically means that you would be re-working the way CakePHP works, so you can forget about ever updating to a newer CakePHP version, and maintenance would be hell.
If you want to modify the system, but keep PHP-Nuke, I'd advise against jamming CakePHP in there, since that would open up too many problems to be able to predict beforehand.
I think your options are as follows:
Learn how PHP-Nuke works so you can modify it
Use regular php for the pages
Either of those are easier by orders of magnitude compared to what you wanted to do.
So to sum up solution I found, if someone will be looking for something similar. Problem solved by using two custom route classes ( http://manual.cakephp.neoboots.com/2.0/en/development/routing.html#custom-route-classes )
class CustomParserRoute extends CakeRoute {
function parse($url) {
if (parent::parse($url) != false) //if default parser has the match continue
{
// call to Router class to do the routing for new url string again,
// if &cakePHP= is in query string, use this, or use default
if ($_GET['cakePHP']) {
$params = Router::parse($_GET['cakePHP']);
} else {
$params = Router::parse("/my_controller");
}
return $params;
}
return false;
}
}
class CustomMatcherRoute extends CakeRoute {
// cusotm mathc function, that generates url string.
// If this route matches the url array, url string is generated
// with usual way and in the end added to url query used by PHPNuke
function match($url) {
$result_url = parent::match($url);
if($result_url!= false) {
$newurl = function_to_generate_custom_query()."&cakePHP=".$result_url;
return $newurl;
} else {
return $result_url;
}
}
}
And then simple configuration in routes php
App::import('Lib', 'CustomParserRoute');
App::import('Lib', 'CustomMatcherRoute');
// entry point to custom routing, if route starts with modules.php it matches
// the url and CustomParserRoute::parse class is called
// and route from query string is processed
Router::connect('/modules.php', array('controller' => 'my_controller'), array('routeClass' => 'CustomParserRoute'));
// actual routes used by cakephp app, usual routes that need to use
// CustomMatcherRoute classe, so when new url is generated, it is modified
// to be handled later by route defined above.
Router::connect('/my_controller/:action/*', array('controller' => 'my_controller'), array('routeClass' => 'CustomMatcherRoute'));

Categories