Get url of document node


(Robotic Brain) #1

Hi,
Is there any way to generate the absolute URL to a node from the Backend?
(Or its parent Page)

I want to write a small Webmention Client and need the URL of a given node from the
TYPO3\TYPO3CR\Domain\Model\Workspace -> afterNodePublishing Signal, so i can send it to the Webmention Server.

Being able to use \TYPO3\Neos\Service\LinkingService would be nice but i don’t know how to get the necessary ControllerContext (DI gives me some weird error about missing constructor arguments - i guess that means there is no ControllerContext yet?)

Is that possible within this context at all?

Cheers
Robotic-Brain


(Sebastian Kurfuerst) #2

Hey Robotic-Brain,

could you share some more context where you need this URL? In a controller, ViewHelper, Plugin, …? There is, for example, the <neos:link.node> ViewHelper which does what you want.

All the best,
Sebastian


(Robotic Brain) #3

To be honest I don’t really know the context…
I setup a Signal Handler in the Package boot function for TYPO3\TYPO3CR\Domain\Model\Workspace::emitAfterNodePublishing
That calls a helper object, which filters the signal a bit and then calls my Webmention Service class via a property injected instance.

The only class i’m subclassing is TYPO3\Flow\Package\Package

I guess that means i’m in whichever context, the workspace is used in?

here the relevant code:

Package.php:

class Package extends BasePackage {
	public function boot(\TYPO3\Flow\Core\Bootstrap $bootstrap) {
		$dispatcher = $bootstrap->getSignalSlotDispatcher();
		$dispatcher->connect(
			'TYPO3\TYPO3CR\Domain\Model\Workspace', 'afterNodePublishing',
			'RoboticBrain\Webmention\Slot\SlotHandler', 'onNodePublished'
		);
	}
}

SlotHandler.php:

class SlotHandler {
	/**
	 * @var \RoboticBrain\Webmention\Service\Webmention
	 * @Flow\Inject
	 */
	protected $webmentionService;

	public function onNodePublished(NodeInterface $node, Workspace $targetWorkspace) {
		if ($targetWorkspace->getName() !== 'live') {
			return;
		}
		$this->webmentionService->sendForNode($node);
	}
}

Webmention.php:

class Webmention {
	public function sendForNode(NodeInterface $node) {
		// i need the node URL here
	}
}

(Robotic Brain) #4

Hello,
I found this snippet: https://gist.github.com/agitso/5037717
Which helped a bit…
However now I get “@user-admin” appended to all my urls…
Is there no way to simulate a frontend request from the backend?

Cheers
Robotic-Brain


(Christian Müller) #5

You need to make sure to adapt the context of the node to be a frontend context. easiest might be a to use a FlowQuery and the context operation to modify the workspace to live.

Other alternative would be to use the ContextFactory to create a live context and then use the NodeData object of this node and the NodeFactory to create a live node.


(Robotic Brain) #6

I’ve never used FlowQuery from PHP (I’m fresh to Neos) could you give me a small example how to use it please?

Meanwhile i managed to “brute force” my way to the right context (see below)
imho this is way too much code for something that should be as simple as reading a property… :frowning:

/**
	 * @Flow\Inject
	 * @var \TYPO3\Neos\Service\LinkingService
	 */
	protected $linkingService;
	
	/**
	 * @Flow\Inject
	 * @var \TYPO3\TYPO3CR\Domain\Service\ContextFactoryInterface
	 */
	protected $contextFactory;
	
	/**
	 * @var \TYPO3\Flow\Mvc\Routing\UriBuilder
	 */
	protected $uriBuilder;
	
	/**
	 * @link https://gist.github.com/agitso/5037717#gistcomment-1475950
	 * @param \TYPO3\Flow\Configuration\ConfigurationManager $configurationManager
	 */
	public function injectUriBuilder(\TYPO3\Flow\Configuration\ConfigurationManager     $configurationManager) {
		$httpRequest = \TYPO3\Flow\Http\Request::createFromEnvironment();
		$request = new \TYPO3\Flow\Mvc\ActionRequest($httpRequest);
		$uriBuilder = new \TYPO3\Flow\Mvc\Routing\UriBuilder();
		$uriBuilder->setRequest($request);
		$uriBuilder->setCreateAbsoluteUri(true);
		$this->uriBuilder = $uriBuilder;
	}
	
	protected function getUrlToNode(NodeInterface $node) {
		$contentContext = $this->contextFactory->create(array(
			'workspaceName' => 'live',
			'currentSite' => $node->getContext()->getCurrentSite()
		));
		$liveNode = $contentContext->getNodeByIdentifier($node->getIdentifier());
			
		$uri = $this->linkingService->createNodeUri(
				new ControllerContext(
						$this->uriBuilder->getRequest(),
						new \TYPO3\Flow\Http\Response(),
						new \TYPO3\Flow\Mvc\Controller\Arguments(array()),
						$this->uriBuilder
				),
				$liveNode,
				$contentContext->getRootNode(),
				'html',
				true
		);
	}

EDIT: I think i figured it out already:

$liveNode = (new FlowQuery(array($node)))->context(array('workspaceName' => 'live'))->get(0)

Is this correct?
This is much better, than the previous solution :smile:
Thank you!


(Christian Müller) #7

Yes, great that you figured it. :slight_smile:


(George Dimitriadis) #8

IMHO, your getUrlToNode is missing out from core helper functions.

I would expect to be able to call a method like $node->getPublishedUri() to any instance of Neos\ContentRepository\Domain\Model\Node. If the node is just content, then the document that contains it would be loaded, etc, but currently you’d have to build a function like the above to get a universal solution.

Thanks a lot for sharing a solution, I had the same problem in signal-level functionality.

Updating for 2018:

  1. Replace TYPO3 with Neos
  2. Replace TYPO3CR with ContentRepository
  3. Get rid of ‘currentSite’ argument, getCurrentSite method does not exist.