Front-End Plug-In Page - url-rewriting

Ok, so I've looked at many posts on here about similar topics, but every single one of them is a rewrite rule for an actual php file in the url such as cart.php rather than the pretty permalink /cart/.
Basically, all I'm trying to do here is have WordPress include a template whenever a specific url is visited. In this case, example.com/cart/. Since this code is for a plug-in, it's a url that does not exist, and I cannot create a page for it. I'm trying to use a rewrite rule, along with query vars, to display the correct template when the user visits /cart/. I think I'm pretty close, but I've tried it several different ways, and I know I'm probably missing something obvious. I keep getting a 404 error, and I'm pretty sure it's my rewrite rule that is the problem. Can you guys give me a hand with this one?
Code:
/* Rewrite Rules */
add_action('init', 'llc_product_rewrite_rule');
function llc_product_rewrite_rule() {
add_rewrite_rule( 'cart/', 'index.php?llc_page=cart', 'top' );
}
/* Query Vars */
add_filter( 'query_vars', 'llc_product_register_query_var' );
function llc_product_register_query_var( $vars ) {
$vars[] = 'llc_page';
return $vars;
}
/* Parse Request */
add_action('parse_request', 'llc_cart_template');
function llc_cart_template() {
if (get_query_var('llc_page') && (get_query_var('llc_page') == "cart")) {
include plugin_dir_path(__FILE__).'inc/cart.php';
exit();
}
return;
}

Thank you for all of your well thought out responses you guys, but unfortunately I don't have all day and had to go through trial and error hell to figure this one out.
So firstly, if you want to simply have example.com/cart/ load a specific page template, when the page does not exist (for example if you need your wordpress plug-in to generate the page), this is how you'll need to set up your code. To clarify what's different from the code in my question, it's the rewrite rule, and the way I included the template. I used template_include rather than parse_request.
/* Rewrite Rules */
add_action('init', 'llc_product_rewrite_rule');
function llc_product_rewrite_rule() {
add_rewrite_rule( 'cart/?$', 'index.php?llc_page=cart', 'top' );
}
/* Query Vars */
add_filter( 'query_vars', 'llc_product_register_query_var' );
function llc_product_register_query_var( $vars ) {
$vars[] = 'llc_page';
return $vars;
}
/* Template Include */
add_filter('template_include', 'llc_cart_template_include', 1, 1);
function llc_cart_template_include($template)
{
global $wp_query;
$llc_page_value = $wp_query->query_vars['llc_page'];
if ($llc_page_value && $llc_page_value == "cart") {
return plugin_dir_path(__FILE__).'inc/cart.php';
}
return $template;
}

Related

Static controller multiple routes/single controller

I tried to find an answer that does the job, but the ones I found didn't really feel right to me, I have a static part of my site which I can arrange the route normally as in the code below.
it seems so messy to repeat the same route for more 7 pages, there got to be a smarter way to do it, I need to some opinions, maybe a controller with a slug or something, but I don't quite understand how to do it with a static part of the site like this.
Thanks.
Route::get('/first/home', function()
{
return View::make('/first.home');
});
Route::get('/first/aboutus', function()
{
return View::make('/first.aboutus');
});
Route::get('/first/contact', function()
{
return View::make('/first.contact');
});
I have a feeling your coming from WP, or maybe i'm wrong. In anycase, Laravel has a diff. ballgame in terms of serving static pages.
Now after your clarification, I would recommend you group your routes, as follows:
Route::group(['prefix' => 'first'], function () {
Route::get('(.*)', 'HomeController#getPageByName');
});
in HomeController You can write the following function:
function getPageByName(Request $request)
{
$pageName = $request->path();
//since your url does not begin with the page but with 'first',
//best is to turn this into an array.
$parsedPath = explode("/",$pageName)
$data = 'any data you want to pass';
if(View::exists($pageName[1] ){
return view($pageName[1] , compact($data));
} else {
abort(404)
}
}
Essentially your url would look like this, if you want page one:
http://example.com/first/home
Now Laravel will look for views/home.blade
Let me know if this helps your issue.

Vuex and Laravel 5: how to remove Tags from Articles

I have a small blog app that has Articles and Tags. Nothing fancy so far. Every Article can have many Tags.
The Laravel backend delivers the data via API calls from Axios in the Vue Frontend. In the Laravel models Article has a method
public function tags(){
return $this->belongsToMany('App\Tag');
}
and vice versa for tags. I have a pivot table and all this follow pretty much the example given in https://laracasts.com/series/laravel-5-fundamentals/episodes/21
All this works fine.
Now let´s say I want to call in Vue the method deleteTag() which should remove the connection between Article and Tag. Things are behind the scenes a bit more complicated as "addTag" in PHP also adds a new Tag Model AND the connection between Tag and Article in the Pivot table OR connects - if the Tag exists already - an existing Tag with Article.
What is the best way to achieve this?
What I´m doing so far:
ArticleTags.vue
deleteTag(tagName){
let articleId = this.articleId;
this.$store.dispatch('removeTagFromArticle', { articleId, tagName });
},
index.js (Vuex store)
actions: {
removeTagFromArticle(context,param) {
axios.post('api/articles/delete-tag/', param)
.then((res) => {
let article = context.getters.getArticleById(param.articleId);
let tagName = param.tagName;
context.commit('deleteTag', {article, tagName} );
})
.catch((err) => console.error(err));
} }
mutations : { deleteTag (state, { article, tag }) {
let tagIndex = article.tags.indexOf(tag);
article.tags.splice(tagIndex,1);
} }
ArticleController.php
/**
* Remove Tag from Article
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function deleteTag(Request $request)
{
$tag = \App\Tag::firstOrCreate(array('name' => $request->tagName));
$article = Article::findOrFail($request->articleId);
$result = $article->tags()->detach([$tag->id]);
$this->cleanUpTags();
return response('true', 200);
}
routes/web.php
Route::post('api/articles/delete-tag', 'ArticleController#deleteTag');
This works so far. The code does exactly what it should. Only it feels really clumsy. And probably to complicated. Maybe it´s because the example is simple but the whole setup is big.
Nonetheless I´m trying to improve my coding. :)
So my questions are:
1) Would it be better to pass the article object in Vue to the store instead of the articleId?
2) Is the idea of using Array.slice() in the store too complicated? This could be done straight in the components.
3) Does it make sense to reload the whole store from Laravel after deleting the tag PHP-wise?
Edit: in case someone is looking for this question and how I solved it at the end. The source code for this app can be found at https://github.com/shopfreelancer/streamyourconsciousness-laravel
Personally I like to use ID's to reference any database resource aswell as keeping my objects in javascript somewhat the same as my API.
1
In this case I would have changed my tags to objects instead of strings and send an ID of the tag to my API.
An example of my article would look like:
let article = {
tags: [{ id: 1, name: 'tag 1' }, { id: 2 ... }]
}
Using objects or IDs as parameters are in my opinion both fine. I should stick with objects if you like "cleaner" code, there will only be one place to check if the ID is present in your object aswell as the selection of the ID.
Case:
// Somehwere in code
this.deleteArticle(article);
// Somehwere else in code
this.deleteArticle(article);
// Somewhere else in code
this.deleteArticle(article);
// Function
deleteArticle(article) {
// This check or other code will only be here instead of 3 times in code
if (!article.hasOwnProperty('id')) {
console.error('No id!');
}
let id = article.id;
...
}
2
Normally I would keep the logic of changing variables in the components where they are first initialized. So where you first tell this.article = someApiData;. Have a function in there that handles the final removal of the deleted tag.
3
If you are looking for ultimate world domination performance I would remove the tag in javascript. You could also just send the updated list of tags back in the response and update all tags of the article with this data and keep your code slim. However I still like to slice() the deleted tag from the array.
Remember this is my opinion. Your choises are completely fine and you should, like I do myself, never stop questioning yours and others code.

How to render a cms page with default theme AND variables from controllers in OctoberCMS?

I'm wondering how I can render a view, or display a page with my default theme in OctoberCMS, via a route that executes a function in a controller.
If I have the following route:
Route::get('bransje', [
'uses' => 'Ekstremedia\Cityportal\CPController#bransje'
]);
And in my controller CPController ive tried several things, like I used to with Laravel:
public function bransje() {
$stuff = Stuff::with('info');
return View::make('cms::bransje')->with('stuff',$stuff);
}
But I cannot seem to get it to work, and I've tried to search the web, but it's hard to find answers. I have found a workaround, and that is to make a plugin component, then I can include that component and do:
public function onRun()
{
$this->eventen = $this->page['stuff'] = $this->stuff();
}
protected function stuff()
{
return ...
}
Is there any way so I can make pages without using the Cms, and that are wrapped in my default theme? I've tried
return View::make('my-theme-name::page');
and a lot of variants but no luck.
I know I can also do a:
==
public function onRun()
{
}
in the start of my page in the cms, but I'm not sure how to call a function from my plugin controller via there.
You can bypass frontend routing by using routes.php file in your plugin.
Full example in this video turotial.
If this answer can still be useful (Worked for October v434).
I have almost the same scenerio.
What I want to achieve is a type of routing like facebook page and profile.
facebook.com/myprofile is the same url structure as facebook.com/mypage
First I create a page in the CMS for each scenario (say catchpage.htm)
Then a created a catchall route at the buttom of routes.php in my plugin that will also not disturb the internal working of octobercms.
if (!Request::is('combine/*') && !Request::is('backend/*') && !Request::is('backend')) {
// Last fail over for looking up slug from the database
Route::get('{slug}/{slug2?}', function ($slug, $slug2 = null) {
//Pretend this are our routes and we can check them against the database
$routes = ["bola", "sade", "bisi", "ade", "tayo"];
if(in_array($slug, $routes)) {
$cmsController = new Cms\Classes\Controller;
return $cmsController->render("/catchpage", ['slug' => $slug]);
}
// Some fallback to 404
return Response::make(View::make('cms::404'), 404);
});
}
The if Request::is check is a list of all the resource that october uses under the hood, please dont remove the combine as it is the combiner route. Remove it and the style and script will not render. Also the backend is the url to the backend, make sure to supply the backend and the backend/*.
Finally don't forget to return Response::make(View::make('cms::404'), 404); if the resource is useless.
You may put all these in a controller though.
If anyone has a better workaround, please let us know.

Codeigniter Page cache with GET parameter

I am newbie to CI cache. I am facing some weird problem with codeigniter page caching. $this->output->cache(300);
I was expecting that cached version would not load if arguments in GET[] would change. But it is loading cache without considering any GET[] parameters.
I have one page where it says whether comment has been saved or not [via get parameter],
/product/product-name/?saved=true redirecting to same page where comment form is located. But it is not working. How can i invalidate old cache and create new one depending upon the get parameter? or i need to change the behavior of my comment system?
Thanks.
EDIT
Should i simply use database cache instead of Web page cache in this case?
You just have to enable the cache_query_string option in the config/config.php file.
$config['cache_query_string'] = TRUE;
Create a cache_override hook to check if there are any GET[] variables set and then skip the cache_override.
[EDIT #1]
Here is an example:
Create this file in your hooks directory:
<?php
class GetChecker {
public function checkForGet()
{
global $OUT, $CFG, $URI;
if (isset($_GET) AND ! empty($_GET))
{
return;
}
if ($OUT->_display_cache($CFG, $URI) == TRUE)
{
exit;
}
}
}
Then add this to the config/hooks.php:
$hook['cache_override'][] = array(
'class' => 'GetChecker',
'function' => 'checkForGet',
'filename' => 'GetChecker.php',
'filepath' => 'hooks'
);
I haven't tested it, it might need a little tweaking to work...
I test on CI 3+ , file system/core/Output.php 559 line, change this
if ($CI->config->item('cache_query_string') && !empty($_SERVER['QUERY_STRING']))
{
$uri .= '?'.$_SERVER['QUERY_STRING'];
}
on this
if ($CI->config->item('cache_query_string') /* && ! empty($_SERVER['QUERY_STRING']) */ && !empty($_REQUEST))
{
// $uri .= '?'.$_SERVER['QUERY_STRING'];
$uri .= '?'.http_build_query($_REQUEST);
}
And add string to your application/config/config.php
$config['cache_query_string'] = true;
it will be work with GET, POST, COOKIE ....
If need only GET, just $config['cache_query_string'] = true; - enough
I found no easier way using Hooks to prevent writing cache, as its calling _write_cache() inside the _display() method itself of CI_Output class.
For quick and easiest solution I added two conditions to display cache and write cache, if Query String parameter has variable defined( offset in my case, as I wanted for pagination)
Edit: system/core/Output.php
Add Condition to prevent writing cache, if specific GET parameter found:
function _write_cache($output)
{
if (isset($_GET['offset']) AND ! empty($_GET['offset']))
{
log_message('debug', " Don't write cache please. As as its matching condition");
return;
}
...
...
}
Add Condition to prevent displaying cache, if specific GET parameter found:
function _display_cache(&$CFG, &$URI)
{
if (isset($_GET['offset']) AND ! empty($_GET['offset']))
{
log_message('debug', " Don't display cache please. As as its matching condition");
return FALSE;
}
...
...
}

Codeigniter global_xss_filtering

In my codeigniter config I have $config['global_xss_filtering'] = TRUE;. In my admin section I have a ckeditor which generates the frontend content.
Everything that is typed and placed inside the editor works fine, images are displayed nice, html is working. All except flash. Whenever I switch to html mode and paste a youtube code piece it is escaped and the code is visible on the frontpage instead of showing a youtube movie.
If I set $config['global_xss_filtering'] = FALSE; the youtube code is passed like it should. This is because 'object', 'embed' etc are flagged as "naughty" by CI and thus escaped.
How can I bypass the xss filtering for this one controller method?
Turn it off by default then enable it for places that really need it.
For example, I have it turned off for all my controllers, then enable it for comments, pages, etc.
One thing you can do is create a MY_Input (or MY_Security in CI 2) like the one in PyroCMS and override the xss_clean method with an exact copy, minus the object|embed| part of the regex.
http://github.com/pyrocms/pyrocms/blob/master/system/pyrocms/libraries/MY_Security.php
It's one hell of a long way around, but it works.
Perhaps we could create a config option could be created listing the bad elements for 2.0?
My case was that I wanted global_xss_filtering to be on by default but sometimes I needed the $_POST (pst you can do this to any global php array e.g. $_GET...) data to be raw as send from the browser, so my solution was to:
open index.php in root folder of the project
added the following line of code $unsanitized_post = $_POST; after $application_folder = 'application'; (line #92)
then whenever I needed the raw $_POST I would do the following:
global $unsanitized_post;
print_r($unsanitized_post);
In CodeIgniter 2.0 the best thing to do is to override the xss_clean on the core CI library, using MY_Security.php put this on application/core folder then using /application/config.php
$config['xss_exclude_uris'] = array('controller/method');
here's the MY_Security.php https://gist.github.com/slick2/39f54a5310e29c5a8387:
<?php
/**
* CodeIgniter version 2
* Note: Put this on your application/core folder
*/
class MY_Security extends CI_Security {
/**
* Method: __construct();
* magic
*/
function __construct()
{
parent::__construct();
}
function xss_clean($str, $is_image = FALSE)
{
$bypass = FALSE;
/**
* By pass controllers set in /application/config/config.php
* config.php
* $config['xss_exclude_uris'] = array('controller/method')
*/
$config = new CI_Config;
$uri = new CI_URI;
$uri->_fetch_uri_string();
$uri->_explode_segments();
$controllers_list = $config->item('xss_exclude_uris');
// we need controller class and method only
if (!empty($controllers_list))
{
$segments = array(0 => NULL, 1 => NULL);
$segments = $uri->segment_array();
if (!empty($segments))
{
if (!empty($segments[1]))
{
$action = $segments[0] . '/' . $segments[1];
}
else
{
$action = $segments[0];
}
if (in_array($action, $controllers_list))
{
$bypass = TRUE;
}
}
// we unset the variable
unset($config);
unset($uri);
}
if ($bypass)
{
return $str;
}
else
{
return parent::xss_clean($str, $is_image);
}
}
}
Simple do the following on the views when displaying embedded object code like from YouTube and etc:
echo str_replace(array('<', '>'), array('<', '>'), $embed_filed);
The global XSS Filtering is only escaping (or converting) certain "dangerous" html tags like <html>
Simple Workaround:
Set $config['global_xss_filtering'] = TRUE;
Run your POST data through HTMLPurifier to remove any nasty <script> tags or javascript.
HTMLPurifier Docs
HTMLPurifier Codeigniter Integration
On the page where you receive the forms POST data use html_entity_decode() to undo what XSS filtering did.
//by decoding first, we remove everything that XSS filter did
//then we encode all characters equally.
$content = html_entity_decode($this->input->post('template_content'))
Then immediately run it through htmlentities()
$content = htmlentities($content);
Store as a Blob in MySQL database
When you want to display the
information to the user for editing run html_entity_decode()
This is how I did it. If anyone knows of a major flaw in what I did, please tell me. It seems to be working fine for me. Haven't had any unexpected errors.

Resources