Jump to content
Orkun

addHookAfter on Pages saveReady (filling up a index(textarea) field for search) causes internal server error when trying to save the pages with the index field per api

Recommended Posts

Hi Guys

This hook works fine. 

$this->addHookAfter('Pages::saveReady', $this, 'hookIndexingBefore');

protected function hookIndexingBefore( HookEvent $event ) {

        $page = $event->arguments("page");

        if(!$page->template->hasField("index")) return;	// no index field in this page/template
        if($page->isNew() || $page->isTrash() || $page->indexSaveFlag) return;

        $language = $this->wire("user")->language;    // save user lang
        $page->index = '';

        foreach($this->wire("languages") as $lang) {

            $this->wire("user")->language = $lang; // change user lang

            wire('pages')->setOutputFormatting(true);
            $content = $page->render(); // render page and get the content
            wire('pages')->setOutputFormatting(false);

            if($content) $content = $this->parseContent($content); //remove html, new lines etc...
            $page->index .= $content;
        }
    

        $page->indexSaveFlag = true; // in case it get's saved again (not case with Pages::saveReady)
        $this->wire("user")->language = $language; // restore user language
}

 

But when I try to save a page per api I get an Internal Server Error 500. When I replace "$page->render();" with "" inside the hook, it doesn't cause a internal server error anymore.

$page->save(); //causes internal server error now

 

Share this post


Link to post
Share on other sites

Maybe I don't fully understand what works and what doesn't in your case, but typically with hooks on after page save you can end up with recursion when you include $page->save inside the hook function. Typically you need to make it a "before" hook, or do: $page->save("index"); so that you are just saving the specific field (not the entire page), so that the hook won't be triggered again.

  • Like 1

Share this post


Link to post
Share on other sites
11 hours ago, Nukro said:

When I replace "$page->render();" with "" inside the hook, it doesn't cause a internal server error anymore.

This makes $content empty, so when you call parseContent() it is getting an empty string. Edit: actually, because of the if() test, parseContent() isn't called at all.

You don't show the parseContent() method in your code, and the problem could be there.

 

  • Like 1

Share this post


Link to post
Share on other sites

Thanks for your Input @adrian and @Robin S

I now changed the addHookAfter to addHookBefore. The indexing is still working. But the problem(internal server error) with saving from api when the hook is active is still consistent. The parseContent function contains some preg_replaces and strip_tags to clean out the html and define the "indexing area" so that the nav and footer content isn't indexed all the time.

parseContent()

protected function parseContent($content){
        $startStr = "<!--### start-indexing-area ###-->";
        $endStr = "<!--### end-indexing-area ###-->";

        preg_match_all('/'.$startStr.'(.*)'.$endStr.'/siU', $content, $matches);

        $newContent = preg_replace("/<div class='breadcrumb.*'>.*<\/div>/siU", '', $matches[1][0]);
        $newContent = str_replace('<', ' <', $newContent);
        $newContent = strip_tags($newContent);
        $newContent = preg_replace("/\s\s+/", " ", $newContent);
        return $newContent;
 }

It's really weird that it is causing a 500 internal server error when saving from api but it isn't causing a 500 internal server error when saving from backend, when the hookIndexingBefore is active.

Share this post


Link to post
Share on other sites

I think it has somehow something to do with the $page->render(). Because when I do this:

$content = <!--### start-indexing-area ###-->This is a sample Text<!--### end-indexing-area ###-->;

The Internal Server Error doesn't appear anymore. But when I save the $page->render() as $content I get the internal server error... It has also no effect when I do $page->render and skip the parseContent section, It still appears an internal server error. 

Share this post


Link to post
Share on other sites

I'm not sure what could be causing the 500 error. The Apache/PHP/PW error logs might shed some light. If this is happening on a live site then it could even be caused by mod_security - turn that off if you can or check with your host.

Rather than using your parseContent function you could consider rendering the page with a different template that is created with the index field in mind.

$page->render($filename); // $filename assumed in /site/templates/

See this post:

Or you could loop over the page's fields in the hook, checking for the type of each field and preparing it's value for the index field accordingly.

foreach($page->fields as $field) {
    /*
    you can get the value...
    $page->get($field->name);
    ...but you'll want to treat it differently based on field type
    if($field->type == 'FieldtypePage') {
        ...etc...
    }
    */
}

 

  • Like 3

Share this post


Link to post
Share on other sites
12 minutes ago, Robin S said:

I'm not sure what could be causing the 500 error. The Apache/PHP/PW error logs might shed some light. If this is happening on a live site then it could even be caused by mod_security - turn that off if you can or check with your host.

Rather than using your parseContent function you could consider rendering the page with a different template that is created with the index field in mind.


$page->render($filename); // $filename assumed in /site/templates/

See this post:

Or you could loop over the page's fields in the hook, checking for the type of each field and preparing it's value for the index field accordingly.


foreach($page->fields as $field) {
    /*
    you can get the value...
    $page->get($field->name);
    ...but you'll want to treat it differently based on field type
    if($field->type == 'FieldtypePage') {
        ...etc...
    }
    */
}

 

Thanks for your Input @Robin S!

I think I will go with the last option since it would solve another problem I have ;D.

Greetings Orkun

Share this post


Link to post
Share on other sites

Hi Guys

I had looked before inside all Apache/PHP/PW log files. There was nothing suspicous. But I have found something else that could be helpful.

I logged my hook so that I can watch what is happening. I have found out that my beforeHook on saveReady is called 4-5 times when I am saving from API and when the $content comes from $page->render(). When I save from API and the $content doesn't come from $page->render() it only calls 1 time like it should be. Does page->render calls saveReady function or something similar?

Share this post


Link to post
Share on other sites

I did a bit of a test. I'm using delayed output with an auto-appended "_main.php". I created a different append file "_main_index.php" for the purposes of rendering the index field: just echos the region variables without any HTML elements. Then in /site/ready.php...

$this->pages->addHookAfter('saveReady', function($event) {
    $page = $event->arguments('page');
    if($page->template->name !== 'basic_page') return;
    $page->index = $this->sanitizer->textarea($page->render(null, ['appendFile' => '_includes/_main_index.php']));
});

All works well, no errors or multiple calls to the hook when saving from the admin or the API. Maybe it's the fact that I'm hooking after saveReady that make the difference.

Edit: hooking before or after saveReady should make no difference. From the hook docs...

Quote

Some hookable methods in ProcessWire have no implementation and exist purely for you to hook. Because hooks like this exist purely "to be hooked", it doesn't matter if you hook before or after them. Examples include Pages::saveReady which is called after it's been determined the page will definitely be saved, but right before it is done.

 

Share this post


Link to post
Share on other sites

@Robin S

I have now made a file inside the root folder and bootstrapped PW in it. Now I don't get any internal server errors anymore. Some pages get saved now from the api until it stops, when the page is not viewable ( which is right ). But I have looked at this pages and they are visible to the guest user? I get errors like this: page "it/page-name" or "de/page-name" is not viewable etc... So i tought the languages are not activated in the page settings but they are all activated?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Hardoman
      Hello community,
      we have a website running version 3.0.118. The owner would like to have a watermark merged to the images, that are being uploaded in the CKEditor as a requirement.
      Image upload besides the CKEditor within galleries and single images works as a charm already. We also use croppable image 3 there. (PIM2)
      To realize this requirement, I thought of using a hook in the admin area. So, I read a lot in our forums and tested this by adding a hook into the ready.php file.
      $this->addHookAfter('InputfieldFile::fileAdded',function(HookEvent$event){ wire('log')->save('test','Image upload works'); ... The log entry is being created correctly. But when I try to use the pim/watermark-function like in a template, he cannot find the watermark-image anymore. Furthermore, when I try to get the page-id, it does not seem to be accessible, because the application does not seem to know how to reference it, or I dont know the right way to do so…
      So my questions are:
       
      Is this the right attempt at all or will there be another, better workaround? It seems, I cannot access the page object (of the content page) within this scope or file but I would need it to save the processed image inside the right files/id folder Would it be better to place the hook into the admin-template? (or admin.php)
        Thanks for any hints in advance. 🙂
    • By VeiJari
      Hello forum, this is my first security related post, so I'm a bit of a newbie.
      I understand that when I have direct front-input from user I should sanitize the input, but how about when I use a secret key for showing a API for a third-party supplier? Should I sanitize the input->get() key?
      I've tested this issue and I tried ?key=<?php echo $page->field; ?> And without adding any sanitization it comes back: /?key=<?php%20echo%20$page->field;%20?>
      So can I rely on this, or should I still use $sanitizer just in case?
       
      Thanks for the help!
    • By EyeDentify
      I have been experimenting with the new $page->meta() method and find it useful.

      Once i figured out that the data i "save" with it is tied to the page where i called the method from.

      So this is not obvious at least not for me in the documentation:
      https://processwire.com/api/ref/page/meta/
       
      So i just wanted to share that revelation with the community so you don´t get as confused as i was.

      Happy Coding Everyone.
    • By louisstephens
      Going through my long quest to get better with ajax and utilizing the api, I have hit yet another roadblock. I currently have a form with an image field (thanks to flydev for getting that sorted), "title" text input, and a select field set to multiple. In my ajax call, I added in:
      tags = $("#select-tags").val(); form_data.append('tags', tags); $.ajax({ type: 'POST', data: form_data, contentType: false, processData: false, url: '/ajax/upload-preview/', success: function(data) { console.log("Woo"); }, error: function(xhr, ajaxOptions, thrownError) { alert(xhr.responseText); } }); And in the ajax template: 
      $tags = $sanitizer->text($_POST['tags']); $image = $sanitizer->text($_POST['image']); $p = new Page(); $p->template = "preview"; $p->parent = $pages->get("/previews/"); $p->name = $title; $p->title = $title; $p->tags = $tags; $p->save(); If I select a "tag" from the select input and submit, it does indeed add it to the Page Reference field in the backend. However, this does not work with an array being passed to it of multiple options.

      So it does appear that my ajax call is trying to submit multiple options, but I am really just unsure how to get these two added in. I saw in other forums posts of add($page) and even add(array()). Do I need to handle this js array differently or do  I need to foreach through the $tags to add it like:
      foreach($tags as $tag) { $p->tags->add($tag); $p->save(); } I tried this approach, but apparently I am still missing something.
       
      Edit:
      I was doing some tweaking, and I know I can split the js array out like:
      for (i = 0, len = tags.length; i < len; i++) { console.log(tags[i]); } However, I am not sure then how to handle the POST in php if I were to split it out.
    • By Tyssen
      I have a page that contains a single ProFields table field and I want to display the contents of the table on the front end and then for logged in users, they can edit certain columns in the table.
      What I have at the moment is
      $out = '<form action="'.$page->url.'" method="post" > <table class="table"> <tbody>'; $count = 1; foreach($page->fieldName as $row) : $out .= ' <tr> <td><input type="checkbox" name="fieldName_'.$count.'_columnName"></td> </tr>'; if($input->post->submit) : $page->of(false); $page->set('fieldName_'.$count.'_columnName', $sanitizer->text($input->post->{fieldName_'.$count.'_columnName})); $page->save(); endif; $count++; endforeach; $out .= ' </tbody> </table> <button class="button" type="submit">Save</button> </form>'; The two problems I have are:
      I get an error trying from $sanitizer->text($input->post->{fieldName_'.$count.'_columnName}), not sure how to make that dynamic.  If I change the above to just a static value, e.g. $page->set('fieldName_1_columnName', 'Testing') and save the form, it's not saving the values to the database. Where am I going wrong?
×
×
  • Create New...