Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jroute between sites #16879

Merged
merged 31 commits into from Mar 9, 2018
Merged

Jroute between sites #16879

merged 31 commits into from Mar 9, 2018

Conversation

izharaazmi
Copy link
Contributor

@izharaazmi izharaazmi commented Jun 27, 2017

Redo of #11060

Most of the work was originally done by @andrepereiradasilva. This is a redo of his work and more additions from me.

Summary of Changes

This PR is for allowing JRoute to generate URL's from admin to site and vice-versa.

This is a current problem especially if you are trying to make links from the backend to the frontend.

So, with this PR, there is no need anymore to make special code just to get the frontend link from the backend. With this PR we just need to use JRoute, like all other links. The same will work to links from frontend to backend. After this PR, no need to hardcode frontend to admin links.

We will be able to use following syntax:

// Build a frontend URL irrespective of in which context (site/admin) the code is running
JRoute::link('site', $url, $xthml = true, $ssl = null);

// Build a backend URL irrespective of in which context (site/admin) the code is running
JRoute::link('administrator', $url, $xthml = true, $ssl = null);

This should not affect the existing JRoute::_() method.

Testing Instructions

  • Test various URLs for different extensions (articles, menu items, etc).
  • You should try with the several combinations of site settings like:
    • SEF/non-SEF
    • With or without URL rewriting
    • With or without multi-language (i.e. with or without language filter plugin enabled).

Test 1: Links from admin to site

  • Use latest staging with sample data from at least two languages (en-GB, fr-FR)
  • Add to isis template index.php file...
$app = JFactory::getApplication();
$app->enqueueMessage('1. Admin to Admin: ' . JRoute::_('index.php'));
$app->enqueueMessage('2. Admin to Admin: ' . JRoute::_('index.php?option=com_content'));
$app->enqueueMessage('3. Admin to Site: ' . JRoute::link('site', 'index.php?option=com_content'));
$app->enqueueMessage('4. Admin to Site (MutilLanguage 1): ' . JRoute::link('site', 'index.php?option=com_content&lang=fr'));
$app->enqueueMessage('5. Admin to Site (MutilLanguage 2): ' . JRoute::link('site', 'index.php?option=com_users&lang=en'));
  • Open any admin page. Check the URL in resulting message are correct.

Test 2: Links from site to admin

  • Add to protostar template index.php file...
$app = JFactory::getApplication();
$app->enqueueMessage('1. Site to Site: ' . JRoute::_('index.php'));
$app->enqueueMessage('2. Site to Site: ' . JRoute::_('index.php?option=com_content'));
$app->enqueueMessage('3. Site to Admin: ' . JRoute::link('administrator', 'index.php?option=com_content'));
  • Open any frontend page. Check the URL in resulting message are correct.

Test 3: Code Review

Do a code review.

Test 4: Current URLs

Check if current URL generated by JRouter are exactly the same, i.e. no change in current URL.
Probably the best is to leave the patch applied for some time, while you do other things and check there are no problems with the current URLs.


Thanks to @mbabker, @ggppdk and @infograf768 for helping out with this one.

Maintainers, to make sure that are no unintended consequences it would be good if this can be also reviewed from someone that understand the Router better than I do.

return static::_($arguments[0], $xhtml, $ssl, $name);
}

return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a trigger_error() call because you in essence have a "call to undefined function" error here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we check for a valid client name here, the behavior would be different when called using undescore function. That function fails silently on invalid router and we need to keep it like that for B/C, imho.

And here we are only checking for missing first parameter coz we need to populate 4th parameter.

Another alternate way that comes to my mind is to not modify the undescore function signature and add another common function that accepts client name as first parameter.

We might then not need the magic function too. Your opinion please.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I hate the use of magic functions. They usually expose unwanted behavior and do not handle error conditions appropriately (in an error state they should do the same thing as the native PHP engine does for whatever that magic function is overloading). Even in the Framework's database repo, I removed __call() and just made "real" stub methods for the shorthand q(), qn(), and e() methods.

So personally I'd just add stub real methods and avoid the magic as a first option. If the magic method is really needed, then it needs to error trap/handle properly.

That function fails silently on invalid router and we need to keep it like that for B/C, imho.

If the system can't generate a route, it shouldn't silently fail over. There needs to be an error raised.

$router->attachBuildRule(array($this, 'preprocessBuildRule'), JRouter::PROCESS_BEFORE);
$router->attachBuildRule(array($this, 'buildRule'), JRouter::PROCESS_DURING);
// We need to make sure we are always using the site router, even if the language plugin is executed in admin app.
$router = $this->app->isClient('site') ? $this->app->getRouter() : JApplicationCms::getInstance('site')->getRouter('site');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just have this always call JApplicationCms::getInstance('site')->getRouter('site'), the singleton instance in JApplicationCms will be the same as the one in JFactory::$application if you're on the frontend anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

@mbabker mbabker added this to Testing/Review in [3.8] Routing Jun 27, 2017
@izharaazmi
Copy link
Contributor Author

@mbabker Please review the changes now. I have kept the magic method and it does not ignore any errors. If you suggest I can also remove that altogether.

*
* @since __DEPLOY_VERSION__
*/
public static function link($client, $url, $xhtml = true, $ssl = null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This signature should be public static function link($url, $client=null, $xhtml = true, $ssl = null) (we should be explicit the default client variable is null rather than making it compulsory and then passing in null in JRoute::_)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From where I see this, the undescore function is the dedicated interface for the cases where the programmer wants to use active client.

This method link() should ideally be used in cases where the specific $client value has the main significance. It will also improve the readability of the code as a bonus.

Therefore, IMO, this signature looks fit for the purpose. If someone still insist on using link() for what _ is meant for, then we've no reason to stop them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then the link method shouldn't have support for injecting $client = null. That logic should move into _.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that makes sense. I'll update it this afternoon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
The Travis failure is probably something unrelated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check just to make sure? The builds for this pull request didn't start failing until this last change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are failing with Routing related issues. I think they are as a result of this PR

@mbabker mbabker changed the base branch from staging to 3.8-dev July 2, 2017 15:52
@mbabker
Copy link
Contributor

mbabker commented Jul 2, 2017

I started digging into the test problems. We're in dependency hell.

Problem 1 - TestMockApplicationCms::getMethods() needs to include the getName method
Problem 2 - For JPaginationTest, need to add this line to configure the application mock in the setUp method (note that there will be some places that need to change the mock return to administrator in that test class)

$app->expects($this->any())->method('getName')->willReturn('site');

Problem 3 - JApplicationCms::getRouter() is static, it cannot be mocked, so we have to work with a real router (or use Reflection and push a mock into the JRoute::$_router property)
Problem 4 - JRouter::getInstance() does not support constructor injection of the application and menu objects for the JRouterSite subclass, so there is no way to inject stuff, meaning that code is falling onto the defaults
Problem 5 - The JRouterSite constructor calls JApplicationCms::getInstance('site'), this tries to create a JApplicationSite object with default behaviors since there isn't a mock application set up in JApplicationCms::$instances, and this ultimately leads up to a session start failure
Problem 6 - I got too deep into this and said "no more" 😆

@wilsonge
Copy link
Contributor

wilsonge commented Jul 2, 2017

So if you change https://github.com/izharaazmi/joomla-cms/blob/c7dd17d70bebe41230c28140612eccce5d727506/libraries/joomla/application/route.php#L87 to be if (!isset(self::$_router[$client])) (i.e. allow $client to be null), then unit tests pass and effectively replicate the current behaviour (admittedly the incorrect behaviour).

@mbabker
Copy link
Contributor

mbabker commented Jul 2, 2017

Well, good thing JRouter isn't abstract then.

@izharaazmi
Copy link
Contributor Author

@mbabker Its post beta4 now. Is there still any change getting this in?

@izharaazmi
Copy link
Contributor Author

Conflicts resolved.

@euismod2336
Copy link
Contributor

I have tested this item ✅ successfully on b3136c4

Love this PR.

Both in frontend as backend the expected url's show up, both with SEF enabled and disabled.


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/16879.

@ghost
Copy link

ghost commented Sep 29, 2017

RTC after two successful tests.

@joomla-cms-bot joomla-cms-bot added the RTC This Pull Request is Ready To Commit label Sep 29, 2017
@mbabker mbabker changed the base branch from staging to 3.9-dev October 15, 2017 15:56
@mbabker mbabker added this to the Joomla 3.9.0 milestone Oct 15, 2017
@ctech-design
Copy link

I have tested this and really need this PR to be added in Joomla soon, It's been month how long do we have to wait. Please consider releasing this soon

@tonypartridge
Copy link
Contributor

@asfaqueali

Unfortunately this cannot be added into joomla! Until 3.9 since new features cannot go into 3.8 as that would break versioning.

@ctech-design
Copy link

@tonypartridge yeah I understand, but its been almost 4 months and release date of 3.9 is not defined so far.

@tonypartridge
Copy link
Contributor

tonypartridge commented Feb 9, 2018 via email

@wilsonge wilsonge merged commit 8a51f23 into joomla:3.9-dev Mar 9, 2018
@joomla-cms-bot joomla-cms-bot added PR-3.9-dev and removed RTC This Pull Request is Ready To Commit labels Mar 9, 2018
@wilsonge
Copy link
Contributor

wilsonge commented Mar 9, 2018

Merged - awesome work :)

@ctech-design
Copy link

@tonypartridge @wilsonge Thanks for merging it. @izharaazmi thanks for this commit.

@tonypartridge
Copy link
Contributor

tonypartridge commented Mar 9, 2018 via email

@izharaazmi izharaazmi deleted the jroute-between-sites branch March 12, 2018 12:21
@wilsonge wilsonge moved this from Testing/Review to Completed in [3.9] General Aug 10, 2018
@bascherz
Copy link

bascherz commented May 2, 2019

Hi guys,

I am not having a successful experience using the new JRoute::link method. It simply doesn't appear to work in my plugin at all. The code is below. The calls to link are commented-out because they simply don't do anything.

// Build article links
$rel_link = str_replace('/administrator','',JRoute::_(ContentHelperRoute::getArticleRoute($article->id,$article->catid)));
$abs_link = str_replace('/administrator','',JURI::base().$rel_link);
$abs_link = str_replace(':/','://',str_replace('//','/',$abs_link));
// $abs_link = JRoute::link('site',$abs_link);
// $rel_link = JRoute::link('site',$rel_link);

As you can see, I still have to remove the "administrator//" part of back end links and for back end links I still get no SEF URL out of JRoute::link. This particular plugin has a feature that allows either an absolute or relative link to be inserted into the template being used to email a notification that an article was just published or updated.

I'm hoping someone here can show me the err of my ways and, if applicable, point me to where I should have posted this comment.

Thanks,
Bruce

@ghost
Copy link

ghost commented May 2, 2019

@bascherz please open a new Issue (link to this PR) as comments on closed Pull Requests didn't get much notice.

@tonypartridge
Copy link
Contributor

@bascherz - Your code is wrong, you are trying to JRoute a JRouted URL you cannot do that, you need to call the url non-routed i.e. with index.php?option=com_content etc. So try removing the JRoute::_() on the $rel_link = . Also you shouldn't need to replace administrator.

@wilsonge
Copy link
Contributor

wilsonge commented May 2, 2019

Documentation for this is available at https://docs.joomla.org/J3.x:Supporting_SEF_URLs_in_your_component#Creating_Frontend_SEF_URLs_in_the_Administrator as of approximately 5 minutes ago :)

@bascherz
Copy link

bascherz commented May 3, 2019

Documentation for this is available at https://docs.joomla.org/J3.x:Supporting_SEF_URLs_in_your_component#Creating_Frontend_SEF_URLs_in_the_Administrator as of approximately 5 minutes ago :)

Somewhat a brute force method, but it does work. Some of my URLs are not SEF (for whatever reason) and that threw me off a bit as well. No need to open an issue @franz-wohlkoenig.

Here's the code now. Category ID apparently is not needed.

$rel_link = JRoute::link('site','index.php?option=com_content&view=article&id='.$article->id);
$abs_link = JURI::base().$rel_link;

Thanks everyone.

@wilsonge
Copy link
Contributor

wilsonge commented May 4, 2019

It's not required but probably helpful to get exactly the same url as the frontend

@infograf768
Copy link
Member

Also missing language ;)

		if ($article->language && Multilanguage::isEnabled())
		{
			// Get the sef prefix for the language
			$query->clear()
				->select('sef')
				->from($db->quoteName('#__languages'))
				->where($db->quoteName('published') . '= 1')
				->where($db->quoteName('lang_code') . ' = ' . $db->quote($article->language));
			$db->setQuery($query);

			$sef = $db->loadResult();

			if ($article->language != '*')
			{
				$lang = '&lang=' . $sef;
			}
		}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
[3.9] General
Completed
Development

Successfully merging this pull request may close these issues.

None yet