Joomla: jroute & navigating back up in menu hierarchy - seo

I have a simple menu hierachy in a Joomla 1.5 site:
[Newsletters] -> [publisher A] -> [Newsletter A1]
-> [Newsletter A2]
-> [publisher B] -> [Newsletter B1]
-> [Newsletter B2]
When navigating this hierarchy using the menu items or breadcrumbs, the SEF URL's are as expected;
/newsletters/pubA.html
/newsletters/pubA/letterA1.html
/newsletters/pubA/letterA2.html
/newsletters/pubB.html
/newsletters/pubB/letterAB.html
/newsletters/pubB/letterAB.html
However, when navigating the hierarchy using links inside the pages, the url's get messed up. Read on for more details :)
The pages are generated by a component. I've implemented a JRouter to generate SEF URL's. With SEF turned on, I can correctly navigate the hierachy using menu items and the standard breadcrumbs.
However, I also want to navigate by links on the pages themselves (for example, by linking to all of a publisher's newsletters on the publisher's page, or by linking back to the publisher's page from a newsletter's page). This works fine when going from publisher to newsletter. The component calls
JRoute::_('index.php?option=' . $option . '&view=newsletter&newsletterid=' . $newsletterId );
and (correctly) generates a URL like:
/newsletters/pubA/letterA1.html
However, when a user is on a newsletter page and wants to go back up to the publisher's page, things go wrong. For some reason the publisher's alias gets added after the newsletter's alias in the URL, as if it was below the newsletter in the hierachy. The component calls
JRoute::_('index.php?option=' . $option . '&view=publisher&publisherid=' . $publisherId );
but that (incorrectly) generates a URL like:
/newsletters/pubA/letterA1/pubA.html
If I navigate forward to a newsletter again from the above URL, then the URL becomes
/newsletters/pubA/letterA1/letterA1.html
/newsletters/pubA/letterA1/pubA.html
/newsletters/pubA/letterA1/letterA1.html
/newsletters/pubA/letterA1/pubA.html
(i.e., it doesn't go deeper than one or two segment(s) 'wrong'.)
Also, please note that navigation does work (meaning, the right page is opened) -- it's just the URL that looks weird.
I don't see how I could generate the expected URL's as shown at the top; there doesn't seem to be a way to specify 'relative to what' the 'first' segment should be. Should I be tapping into JSite::getRouter() somehow?
The router code is pretty straight forward. It's the first time I write a router, so I could have messed up something. I do find it suspicious that ParseRoute is only ever called with a single segment.
function ComponentBuildRoute(&$query)
{
$segments = array();
if (isset($query['view']))
{
if (isset($query['newsletterid']))
{
$alias = { figure out newsletter alias from newsletter id }
$segments[] = $alias;
unset($query['newsletterid']);
}
else if (isset($query['publisherid']))
{
$alias = { figure out publisher alias from publisher id }
$segments[] = $alias;
unset($query['publisherid']);
}
unset($query['view']);
}
return $segments;
}
function ComponentParseRoute($segments)
{
$vars = array();
$id = { try to retrieve newsletter id matching alias in $segments[0] }
if (!empty($id))
{
$vars['view'] = 'newsletter';
$vars['newsletterid'] = $id;
return $vars;
}
$id = { try to retrieve publisher id matching alias in $segments[0] }
if (!empty($id))
{
$vars['view'] = 'publisher';
$vars['publisherid'] = $id;
return $vars;
}
return $vars;
}
I don't want to use an absolute URL because the publisher's menu item sits under the newsletters menu... obviously there must be a way of doing this as both the menu items and the breadcrumb modules figured it out...
Thanks

There is a problem with your ComponentBuildRoute. In the else if block where you deal with publisherid you unset newsletterid. I am not sure if that is the problem, but fixing it would be the first step to get this issue resolved.

Related

Is it possible to change url using vue-router without going to the page?

There is a shop on nuxtjs and on the /catalog page I need to make a "Load more" button. By clicking on it, products should be loaded and the url should be changed to /catalog/page_2 (?page=2 is not suitable).
If I change the url through $router.push nuxt goes to this page, but I need to change the url, but not go anywhere.
Is it possible to somehow undo the reloading but save the changes in the url?
history.pushState copes with the task, but in this case nuxt does not know that the url has changed and when clicking forward / backward in the browser nuxt does not load the goods needed for this page
Paginations logically belong to the main page so It's good to consider them in URL queries, like ?page=2.
also you can use router.replace to change queries.
this.$router.replace({
query: { ...this.$route.query, page: this.page},
})
Do it with this example
https://codesandbox.io/s/withered-darkness-9ezn9?file=/pages/index/_id.vue
Now I can change the filters, categories, and the url changes, but the page does not reload
As you don't want to change the page if you are already on the correct one, check for differences in current page URL first;
const your_query = '?page=2' // containing url params
const currPath = this.$route.fullPath; // containing current path + query params, e.g. '/catalog/?page=2'
const nextPath = `${this.$route.path}?${your_query)}`;
if (currPath !== nextPath) {
//"Abuse" router to change the current's windows url
this.$router.replace(nextPath, undefined, () => {
//If route navigation fails, e.g. due to a same-route navigation or wrong permissions in navigation-guards, handle here
return;
});
}
Alternative would be to directly pass the new query params to the router, as:
this.$router.replace({ query:
page: 2
})

How to create standalone custome page?

I'm looking for a way to create single page model/ standalone single page.
It's like a custom single page for 'About Us', 'Home Page','Our Team',etc.
They are single page with backend options.
Anyone have any idea ?
So you need to create all needed type of files, like route JS file, template file, add info about that file into routes/index.js
example:
create file routes/views/aboutUs.js :
var keystone = require("keystone");
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = "about-us";
locals.title = "About our company";
// Render the view
view.render("aboutUs");
};
create template file templates/aboutUs.pug :
block content
p Our company is super cool. We based it here long time ago
Put all your static content into template with correct syntax and css
Finally make addition to routes/index.js file:
app.get("/aboutUs", routes.views.aboutUs);
if you need to control user access to page also add such string
app.all("/aboutUs*", middleware.requireUser);
And dont forget to restart the app to see changes
That's clearly not what OP is asking for. They're asking if there is a way to create a single ADMIN UI editable page for Home, About Us, and so on. My answer is that I don't believe that is possible with KeystoneJS. Which is annoying, because I have clients that want that and Keystone would be perfect otherwise. Seems the only way to do it is create a list, auto create a record if one doesn't exist, and set "nocreat", and "novelette" on the list.

WHMCS how can I conditionally hide the primary navigation bar on the top of the client pages?

I am developing a web site selling web hosting and domain registration so I am using WHMCS but I am facing a problem I want to make a custom page under WHMCS directory to allow the admin user to change product's details without showing the top navigation bar which created by WHMCS.
Not sure by Admin user, but if you mean you want to remove the primary menu from client area, add this code to a php file inside: whmcs_dir/includes/hooks (say nomenu.php)
add_hook('ClientAreaNavbars', 1, function ()
{
// Get the current navigation bars.
$primaryNavbar = Menu::primaryNavbar();
$secondaryNavbar = Menu::secondaryNavbar();
$children = $primaryNavbar->getChildren();
if (!is_null($children)) {
foreach ($children as $child) {
$primaryNavbar->removeChild($child);
}
}
$children = $secondaryNavbar->getChildren();
if (!is_null($children)) {
foreach ($children as $child) {
$secondaryNavbar->removeChild($child);
}
}
});
Also, add css code to hide the menubar remaining after removing the items:
#main-menu {display: none}
One note though: even if you hide the menu items, if logged in user knows the page link, which is not secret, he can type the URL directly in the address bar to visit it.
For example, to access my domains page in whmcs: http://whmcs-url.com/clientarea.php?action=domains
Your option to have better control is to check the API functions.

Custom 404 template file in Drupal 8

How can I create a custom 404 page in Drupal 8?
I have created a new page(Content) in the backoffice called 404 (node number 100).
I have set it as the 404 default page at Configuration >
Basic site settings.
It works with the content that I have set in the Backoffice.
But now I want it to be editable programatically and I don't know how can I create the overriding file.
I have tried to create mytheme/templates/html--node--100.html.twig and it works only when the request its directly that url (node/100), but it doesn't work when you try a random slug on the URL and drupal has to resolve it. When this happens, drupal is serving me the content that the 404 page has in the backoffice and not in the file that I have just created.
I have tried several files like page--404-html.twig, html--node--404.html.twig, html--page--404.html.twig,... but it doesn't work neither
Can anyone lend me a hand?
page--system--404.html.twig (or the equivalent for other 4xx statuses) no longer works in Drupal 8.3 as the 4xx response handling has changed. You'll now need the core patch from https://www.drupal.org/node/2363987 or a similar custom module hook that adds template suggestions for these pages:
/**
* Implements hook_theme_suggestions_page() to set 40x template suggestions
*/
function MYMODULE_theme_suggestions_page(array $variables) {
$path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
$suggestions = theme_get_suggestions($path_args, 'page');
$http_error_suggestions = [
'system.401' => 'page__401',
'system.403' => 'page__403',
'system.404' => 'page__404',
];
$route_name = \Drupal::routeMatch()->getRouteName();
if (isset($http_error_suggestions[$route_name])) {
$suggestions[] = $http_error_suggestions[$route_name];
}
return $suggestions;
}
EDIT: It's probably nicer to use hook_theme_suggestions_page_alter to modify the suggestions array. See an updated version of this code in https://www.drupal.org/project/fourxx_templates (or https://github.com/ahebrank/fourxx_templates/blob/8.x-1.x/fourxx_templates.module)
The following implementation adds a template suggestion for page, in this case if you create a page--404.html.twig file in your theme, you'll be able to customize the page and works with Drupal 8.5.1
MYTHEME.theme
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function MYTHEME_theme_suggestions_page_alter(&$suggestions, $variables, $hook) {
/**
* 404 template suggestion.
*/
if (!is_null(Drupal::requestStack()->getCurrentRequest()->attributes->get('exception'))) {
$status_code = Drupal::requestStack()->getCurrentRequest()->attributes->get('exception')->getStatusCode();
switch ($status_code) {
case 404: {
$suggestions[] = 'page__' . (string) $status_code;
break;
}
default:
break;
}
}
}
and create a template called page--404.html.twig and override with your stuff.
OR,
if you want to add suggestions for all error pages, just take out the switch statement.
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function MYTHEME_theme_suggestions_page_alter(&$suggestions, $variables) {
/**
* error page template suggestions.
*/
if (!is_null(Drupal::requestStack()->getCurrentRequest()->attributes->get('exception'))) {
$status_code = Drupal::requestStack()->getCurrentRequest()->attributes->get('exception')->getStatusCode();
$suggestions[] = 'page__' . (string) $status_code;
}
}
You can now use page--404.html.twig. Just be sure you don't set a node as the 404 page. Source: https://www.drupal.org/node/2960810
Try page--system--404.html.twig

Get current page request url in handlebars?

Is there a way to the current request url or path in Handlebars? I need to be able to switch what parts of the theme is loaded based on paths. I've tried {{url}} ... no luck. Using latest Stencil with Cornerstone.
I had to do something like this for a project with 3 different category page layouts. Without custom category templates in Stencil, you have to get a little creative.
First, inject the handlebars URL into your category.js file using the BigCommerce's inject handlebar helper seen here. Then parse it so you get only the unique parts, then perform some logic based on what you want to do.
I used the breadcrumb li length as an indicator of how deep I was in the category tree. There is likely a better way, but this is what I thought of first, and it worked just fine.
category.html
{{inject "currentPage" category.url}}
category.js
var pageURL = this.context.currentPage;
var pageURL = pageURL.replace(/\//g," ").replace("http:","").replace("storeurl.mybigcommerce.com","").replace("storeurl.com","").trim();
var catName = pageURL.substr(0,pageURL.indexOf(' '));
console.log('pageURL = ' + pageURL);
console.log('catName = ' + catName);
console.log($('ul.breadcrumbs li').length);
if( $('ul.breadcrumbs li').length == 3 ){
if(catName == "black-decker"){
if($(".cat-img").length){
$(".page").addClass("model-list");
$(".cat-img").hide();
$(".page").append("<div class='model-wrap'><div class='model-catalog' data-reveal-id='myModal'><span class='click-larger'>Click to view larger</span></div></div>");
$(".sidebarBlock-heading").text("Select Your Model Number Below:");
$(".brand-img").each(function(){
$(this).addClass(catName);
});
} else {
$(".page").addClass("model-list");
$(".sidebarBlock-heading").text("Select Your Model Number Below:");
$(".brand-img").each(function(){
$(this).addClass(catName);
});
// make page full width
$(".page-sidebar.cf.Left").addClass("full-width");
}
}
// MORE CODE etc...