wicket - page expired when click previous button - wicket

I use wicket 7 with Stateful pages and every i change to new page i always use code like this:
PageParameters pageParameters = new PageParameters();
setResponsePage(new SecondPage(pageParameters));
Every i change page, a new page version is created and the page ID is increased by one.
But when I'm trying to load the previous page, i click the previous button from the browser, the page always expired.
i found the problem when i use this getApplication in my program i cant load the previous page. if i exclude getApplication i can load the previous page.
public class BasePage extends WebPage {
private ServletContext servletContext;
private boolean developmentMode;
public BasePage() {
NextApp app = (NextApp) getApplication();
servletContext = app.getServletContext();
developmentMode = app.usesDevelopmentConfig();
/** other code **/
}
}
Please help me, how to use getApplication and i can load the previous page too?

PageExpiredException's javadoc says:
Thrown when a {#link Page} instance cannot be found by its id in the page stores. The page may be
* missing because of reasons like:
* <ul>
* <li>the page have never been stored there, e.g. an error occurred during the storing process</li>
* <li>the http session has expired and thus all pages related to this session are erased too</li>
* <li>the page instance has been erased because the store size exceeded</li>
* #see HttpSession#setMaxInactiveInterval(int)
* #see org.apache.wicket.settings.StoreSettings#setMaxSizePerSession(org.apache.wicket.util.lang.Bytes)
* #see NotSerializableException
Check whether any of these is true.
Also I'd recommend you to use: setResponsePage(SecondPage.class,
new PageParameters());

Related

TYPO3: Howto show records from a folder outside the page tree

In my TYPO3 installaion I have configured two websites in two separate page trees. Now I would like to show a record of a custom extension in website A but the record is stored in the page tree of website B.
This is the showAction in my controller:
public function showAction(\Vendor\Extension\Domain\Model\Event $event)
{
$this->view->assign('event', $event);
}
In the repository I disabled the storage page restriction with the following lines of code:
class EventRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
public function initializeObject()
{
$querySettings = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings::class);
$querySettings->setRespectStoragePage(false);
$this->setDefaultQuerySettings($querySettings);
}
}
Showing a record on website A which is stored in the page tree of website A, works without any problems. But as soon as I try to load a record of the page tree of website B on website A it fails.
So is it possible to show records which are stored outside of the page tree?
Your EventRepository has nothing to do with the parameter resolving for the showAction(). This should just work when given an uid of an event.
I suspect you also use routeEnhancers and maybe you chose 'eval: uniqueInSite' for the slug configuration? - in that case try without routeEnhancers to verify.

How to generate a rating meta tag in TYPO3 with caching?

I would like to generate a rich snippet for ratings in my TYPO3 pages. The rating information are fetched via an API, so I need some kind of caching mechanism.
I have some basic knowledge and experience with TYPO3 extensions, but I am not sure about the cleanest solution. I could render the meta tags with the TYPO3 Meta Tag API and cache the fetched information using the TYPO3 Caching Framework.
But I am not sure where to store the logic so that it gets executed at every page visit. I do not want to use a content plugin for obvious reasons. Should I set up a Controller and call the Contoller's function with e.g. some hook?
Thanks for any help!
You may have a look at the tslib/class.tslib_fe.php hooks in TypoScriptFrontendController class.
Choose the correct one (maybe tslib_fe-PostProc) and do something like this :
1) Register hook in ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'][] = \Vendor\Name\Hook\AdditionalMetaData::class . '->addRating';
2) Create your class with the following method
<?php
namespace Vendor\Name\Hook;
class AdditionalMetaData
{
/**
* #param array $parameters
* #return void
*/
public function addRating(&$parameters)
{
/** #var TypoScriptFrontendController $tsfe */
$tsfe = &$parameters['pObj'];
// Do your logic here...
}
}

Returning Array in USER_INT userFunc leads to <!--INT_SCRIPT output

I have a userFunc which I call via
lib.random = USER_INT
lib.random {
userFunc = My\Plugin\UserFunc\Functions->random
}
when I return a Array and try to access it is fails.
<v:variable.set name="random" value="{f:cObject(typoscriptObjectPath: 'lib.random')}" />
{random.max}
When I try to debug out it I get some <!--INT_SCRIPT string
Did anyone know the problem and a Solution?
/e:
I would like to make the problem a little clearer by describing the Szenario.
I have a Plugin with a Login form. When the User logs in I set a JWT with various basic informations (name, email).
This Informations have to be displayed on various places around the Website, not only on one page (for example profile page). Some cases are prefilled forms or just silly "Hello, Paul" stuff.
So when I first log in (Fresh browser, no cache) then I read "Hello, Paul" after I log out and log in with a another Account (Lets call it "Peter") then It still is written "Hello, Paul" , nor "Hello, Peter". When I clear my browser Cache then everything is fine.
Maybe this helps maybe to solve my dilemma. :)
TL;DR: uncached parts in TYPO3 are replaced in the generated page output string using markers and cannot communicate in the direction intended here. Selectively caching, disabling cache or detaching the data from the main request (with XHR or other) are the only possible methods.
It should be clear that USER_INT achieves its functionality by string replacement in the generated page body. This means, among other things, that:
You can never pass the output of a USER_INT to anything in Fluid, not even if the entire page is uncached. You will effectively be passing a string containing <!---INT_SCRIPT... (the entire marker).
You can however generate USER_INT from Fluid, which ends up in the generated page, which is then replaced with the rendered object (use f:cObject to render a USER_INT or COA_INT).
Then there are the use case context considerations. First of all, a cookie (in practice) changes the request parameters and should be part of the cache identifier that your page uses (it is not this way by default). Second, if said cookie changes the way the page renders (and it does, given your use case) this will cause trouble when the page is cached. Third, the page output changing based on a cookie indicates perhaps sensitive information or at the very least user-specific information.
Taking the above into account your use case should do one of the following things:
Either render the entire chunk of output that changes based on cookie, as USER_INT. That means wrapping the entire Fluid output and rendering it all without caching. Note that template compiling still happens (and you can use f:cache.static to hard-cache certain parts if they never change based on request parameters).
Or add the cookie value to the cHash (page hash value) so that having the cookie set means you request a specific cached version that matches the cookie. This is the preferred way if your cookie's values is generally the same for many users (e.g. it contains a selected contact person from a limited list and stores that in a cookie).
Or, in the case that your output contains actually sensitive information, require that the content element or page is only available when logged in with a specific group. This has two purposes: first, it protects the page from being viewed without authentication - but second, it also makes the page content not cache or be cached with the frontend user group ID as part of the cache identity.
Refactor to XHR request and make whichever endpoint it uses, a USER_INT or manually disabled cache context, then load the data. Or set the actual data in the cookie, then use JS to insert the values where needed.
Hopefully that clarifies the different contexts and why they can't communicate in the direction you're attempting; even if they had been exchanging strings instead of arrays.
See also: .cache sub-object in TypoScript which is where you would be able to craft a unique cache identifier for use case 2 described above.
USER_INT are not Cached, so the values for this are replaced after the cache is build up.
I think f:cObject is the wrong way. Implement an own ViewHelper to get the same data should be an better way.
<?php
namespace My\Plugin\ViewHelpers;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class RandomViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* #var boolean
*/
protected $escapeOutput = false;
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param RenderingContextInterface $renderingContext
* #return string
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
return rand();
}
}
Now you can use it like following:
{my:random()} or <my:random />

Why is redirect resetting my object?

I made a frontend extension with Extbase in TYPO3 6.2 and while redirecting in my controller I'm loosing changes I've made to my object.
I wonder if this is intended and why?
Here I see the change I've made to appointment in the var_dump.
/**
*
* #param Domain\Model\Appointment $appointment
* #return void
*/
public function bookAction(Domain\Model\Appointment $appointment) {
if ($appointment->getBooked()) {
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($appointment);
$this->redirect('update', null, null, array('appointment'=>$appointment));
}
}
Then I see the original object before the changes I've made to appointment in the var_dump.
It seems like the passing of the changed appointment resets it back to its original state...?
/**
* action update
*
* #param Domain\Model\Appointment $appointment
* #return void
*/
public function updateAction(Domain\Model\Appointment $appointment) {
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($appointment);
}
Extbase controllers contain two methods of calling different action within your current action: redirect() and forward().
The difference is tiny, but consequences can be huge.
redirect() calls a different action via 30x HTTP redirect, so basically it requires complete page reload with restoring (and re-initializing) PHP session, data and objects.
Internally Extbase passes just an object's id to a second action, meaning, that in that second action your object is fetched from persistence again. And if the changes were not persisted in previous action, they'll be lost.
forward() just terminates the current MVC request and starts a new one without a page reload, meaning that all the session data and not-peristed changes are still available in a second action.
In this case Extbase passes not an id, but real object, so the changes are still there.
You can do one of the following:
Use forward() instead of redirect().
Persist changes to db via PersistenceManager before calling redirect().
Preserve your object changes somehow (e.g. pass not a real instance to redirect(), but serialized string and then unserialize it in your second action).
I don't see any code where you actually persist anything. So you need that in your update action
$persistenceManager = $this->objectManager->get("TYPO3\\CMS\\Extbase\\Persistence\\Generic\\PersistenceManager");
$persistenceManager->persistAll();
Just changing the object without persisting it won't change anything!

How can I add simple CMS functionality to existing Symfony application

I have an existing web application accessing a MySQL database. I'm porting this application to Symfony. The new application has to use the old database, as we cannot port the whole application at once, i.e. the old and the new application are accessing the same database and the applications are running simultaneously.
The old application had a simple CMS functionality which has to be ported:
There is a table pagewhich represents a page tree. Every page has a slug field. The URL path consists of those slugs representing the path identifying the page node, e.g. "/[parent-slug]/[child-slug]".
The page table also contains a content field. As I already mentioned, the CMS functionality is very simple, so the content is just rendered as page content inside a page layout. The page entry also specifies the page layout / template.
My problem is that I don't know how to set up the routing. In a normal Symfony application I'd know the URL patterns before, but in this case they are dynamic. Also routes cannot be cached, because they could be changed any time by the user. I wonder if I have to drop Symfony's routing completely and implement something on my own. But how?
Now I found Symfony CMF which tells a lot about the framework VS CMS routing conflict. So first, I thought this would be the right way. However the tutorials aim at building an entirely new application based on PHPRC. I wasn't able to derive the tutorial's concepts to my use case.
since you run several URL rules on one symfony application, you will need to work with url prefixes. Either your cms should work with a prefix /cms/parent-slug/child-slug or all other controllers. Otherwise you are not able to differ which controller is meant when a dynamic request arrives.
You can try a workaround with a KernelControllerListener. He will catch up every request and then check if a cms page is requested. On the basis of the request you can set controller and action by yourself. Concept:
Create only one route with "/". Abandon oll other rules. Then create a Listener like this:
<?php
namespace AppBundle\Listener;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
/**
* Class KernelControllerListener
* #package ApiBundle\Listener
*/
class KernelControllerListener
{
/**
* #var CmsRepository
*/
private $requestParser;
/**
* KernelControllerListener constructor.
* #param CmsRepository $CmsRepository
*/
public function __construct(CmsRepository $CmsRepository)
{
$this->CmsRepository = $CmsRepository;
}
/**
* #param FilterControllerEvent $event
*/
public function onKernelController(FilterControllerEvent $event){
$request = $event->getRequest();
//should be /parent-slug/children/slug or any other path
$path = $request->getPathInfo();
if($this->CmsRepository->getCmsControllerIfMatch($path)){
//cms repository search in db for page with this path, otherwise return false
$event->setController([AppBundle\CmsController::class, 'cmsAction']);
return;
}
//repeat if clause for any other application part
}
}
in services.yml:
app.controller_listener:
class: AppBundle\Listener\KernelControllerListener
arguments:
- "#app.cms_repository"
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
Edit: catch all routes, see https://www.jverdeyen.be/symfony2/symfony-catch-all-route/
The question is: Do you whant to migrate the data or not. For both question, the CMF can be an answer. If you wanna a simple dynamic router, you should have a look into the ChainRouter with an custom router definition:
https://symfony.com/doc/current/cmf/bundles/routing/dynamic.html
and
https://symfony.com/doc/current/cmf/components/routing/chain.html
If you wanna migrate the data, you can use fixture loaders, as we use in almost all of our examples.