robinc

Non db dependent 'offline' page - for when pw cant reach the db

Recommended Posts

With any website, there is the possibility of db issues - overloaded server, network connectivity if the db is on another machine in the hosting network, etc.

I would love to see a feature where if there is any reason the db fails or cannot be accessed, then pw displays a dedicated page that is stored in the filesystem - instead of displaying nothing, or an ugly mysql error. Obviously it would be good to log the error, and possibly send a notification to the admin (email?).

This gives us the opportunity to still present a professional front (albeit with no functionality) while problems are resolved behind the scenes. I cannot think of a company I have worked for that hasn't had db errors at times

What are your thoughts?

  • Like 2

Share this post


Link to post
Share on other sites
34 minutes ago, robinc said:

Obviously it would be good to log the error, and possibly send a notification to the admin (email?).

Database errors are always logged. You will find entry in your /site/assets/logs/errors.txt file

2017-03-18 16:05:27    ?    http://example.org/?/    Error:     Exception: SQLSTATE[HY000] [1049] Unknown database 'example' (in .../wire/core/ProcessWire.php line 375)

Depending on your config.php settings an Email will be sent

/**
 * Admin email address
 *
 * Optional email address to send fatal error notifications to.
 *
 * #input email
 * @var string
 *
 */
$config->adminEmail = '';

In case of fatal errors the following html file will be send

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
    <title>500 Internal Server Error</title>
</head>
<body>
    <h1>Internal Server Error</h1>
    <p>The server encountered an internal error or misconfiguration and was unable to complete your request.</p>
    <p>{message}</p>
</body>
</html>

You can modify it. You'll find the file (default installation) in /site/templates/errors/500.html

  • Like 10

Share this post


Link to post
Share on other sites

It would be nice to have an admin interface for managing this file (and linked css). Potentially with wysiwyg editing and preview function, any maybe an 'export from page' option, to create the static file.

 

Share this post


Link to post
Share on other sites

You can create a page with a ckeditor field and then hook on save to rewrite the static file. No time to give you code now.

Share this post


Link to post
Share on other sites

And here goes some code. Place this on site/ready.php (or create a module from it) and edit the first lines to your liking:

 

/*************************/
/******* EDIT HERE *******/
/*************************/

// ID of the page where the custom content is
$this->pageID = 1;

// name of the field where the custom content is
$this->fieldName = 'body';

// name of html tag and id from the parent of the custom content on the 505 file
$this->elementTag = 'div';
$this->elementID = 'message';

/*************************/
/******* END EDIT ********/
/*************************/


// regex to get the contents of parent element
$this->regex = '/(<'.$this->elementTag.'\sid=("|\')?'.$this->elementID.'("|\')?>)([^<]|<.+>.*<\/.+>)+(<\/'.$this->elementTag.'>)/i';

// On save the content of the field on the 505.html file
$this->addHookAfter('Pages::saved', function(HookEvent $event) {
  
    $page = $event->arguments[0];

    if($page->id !== $this->pageID) return;

    $file = wire('config')->paths->templates . '/errors/500.html';

    $content = file_get_contents($file);
    $field = $this->fieldName;

    $content = preg_replace($this->regex, '$1'.$page->$field.'$3', $content);

    file_put_contents($file, $content);

});

// On render the edit page, save the content of 505.html file on the field to show it
$this->addHookBefore("ProcessPageEdit::buildFormContent", function(HookEvent $event) {
  
    $page = $event->object->getPage();

    if($page->id !== $this->pageID) return;

    $file = wire('config')->paths->templates . '/errors/500.html';

    $content = file_get_contents($file);

    preg_match($this->regex, $content, $matches);

    if($matches) {

        $page->of(false);
        $field = $this->fieldName;
        $page->$field = $matches[0];
        $page->save($this->fieldName);

    }

});

 

The above code assumes that your "505.html" file has a <div> with the id "message" in it, you just have to adapt it to yours.

  • Like 2

Share this post


Link to post
Share on other sites

one thing that is not working for me with the offline files in the template folder:

it seems that if you use delayed output, then there is something in the output buffer already that gets prepended to the html file when rendered;

not sure how to fix this, but it's kind of a problem, can't render a clean 500 error page because of this, due to a bunch of markup being output above the opening <html of the error file...

Share this post


Link to post
Share on other sites
On 3/28/2017 at 2:48 AM, diogo said:

And here goes some code. Place this on site/ready.php (or create a module from it) and edit the first lines to your liking:

 


/*************************/
/******* EDIT HERE *******/
/*************************/

// ID of the page where the custom content is
$this->pageID = 1;

// name of the field where the custom content is
$this->fieldName = 'body';

// name of html tag and id from the parent of the custom content on the 505 file
$this->elementTag = 'div';
$this->elementID = 'message';

/*************************/
/******* END EDIT ********/
/*************************/


// regex to get the contents of parent element
$this->regex = '/(<'.$this->elementTag.'\sid=("|\')?'.$this->elementID.'("|\')?>)([^<]|<.+>.*<\/.+>)+(<\/'.$this->elementTag.'>)/i';

// On save the content of the field on the 505.html file
$this->addHookAfter('Pages::saved', function(HookEvent $event) {
  
    $page = $event->arguments[0];

    if($page->id !== $this->pageID) return;

    $file = wire('config')->paths->templates . '/errors/500.html';

    $content = file_get_contents($file);
    $field = $this->fieldName;

    $content = preg_replace($this->regex, '$1'.$page->$field.'$3', $content);

    file_put_contents($file, $content);

});

// On render the edit page, save the content of 505.html file on the field to show it
$this->addHookBefore("ProcessPageEdit::buildFormContent", function(HookEvent $event) {
  
    $page = $event->object->getPage();

    if($page->id !== $this->pageID) return;

    $file = wire('config')->paths->templates . '/errors/500.html';

    $content = file_get_contents($file);

    preg_match($this->regex, $content, $matches);

    if($matches) {

        $page->of(false);
        $field = $this->fieldName;
        $page->$field = $matches[0];
        $page->save($this->fieldName);

    }

});

 

The above code assumes that your "505.html" file has a <div> with the id "message" in it, you just have to adapt it to yours.

Thanks @diogo  for this.

___________________________________
Get Assignment help.

Edited by adrian
Removed spammy link
  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By creativejay
      I'm displaying a list of products which are found by their templates, but the pages are taking a very long time to load. At first, I blamed it on my image rendering (using PIM2), but even with all those images now stored in the file tree, the page is taking abysmally long to load. ProCache seems to help but I don't feel as though what I'm trying to do should be gnawing the bones of my resources quite so long.
      The variable for the selector is defined in my header include:
      $productCatList="prod_series|prod_series_ethernet|prod_series_access|prod_series_accessories|prod_series_fiber|prod_series_pwr_supplies|prod_series_pwr_systems|prod_series_wireless"; $getCurrentProdOptions="template=$productCatList, prod_status_pages!=1554|1559|1560|4242"; Then in the template for the page upon which the directory loads:
      $products = $pages->find("$getCurrentProdOptions"); include_once("./prod-list-row.inc"); echo $out; And the prod-list-row.inc foreach (which is on every page that's exhibiting the slowdown):
      <?php $sum = 0; $out =""; $out .= "<div class='span_12_of_12'>\n"; foreach($products as $p){ $sum += 1; if ($sum % 2 == 0) { $bgcolor = '#fff'; } else { $bgcolor = '#e4e4e4';} $par = $p->parent; $out .="<div class='section group' style='background: $bgcolor ; min-height: 110px'>\n"; $img = $p->prod_image; $thumb = $img->pim2Load('squarethumb100')->canvas(100,100,array(0,0,0,0),'north',0)->pimSave()->url; $out .="<div data-match-height='{$p->title}' class='col span_2_of_12 hide'>"; $out .="<a href='{$p->url}'><span class='product-image-box'><img src='{$thumb}' alt='{$p->title}' title='{$p->title}'></span></a>"; $out .= "</div>"; $out .= "<div data-match-height='{$p->title}' class='col span_6_of_12'>"; $out .= "<div class='prod-list-name-label'><a href='{$p->url}'>{$p->title}</a></div>"; if($page!=$par) { $out .= "<div class='prod-list-category-label' style='font-size: .7em;'>Category: <a href='{$par->url}'>{$par->title}</a></div>"; } $out .= "<div class='list-headline' style='font-size: .8em;'>{$p->headline}</div>"; $out .="<div class='learn-more-buttons-sm'>"; $out .="<a href='{$p->url}' title='Product Specs and Documentation'><span class='find-out-more-button' style='font-size: .8em;'><i style='font-size: .8em;' class='fa fa-lightbulb-o' ></i> &nbsp; Learn More</span></a>"; $out .="</div>"; $out .="</div> \n"; $out .= " <div data-match-height='{$p->title}' class='col span_4_of_12'>"; if(count($p->prod_feat_imgs) >0 ){ $out .= "<div class='featured-icons-list' margin: 2em .5em;'>"; foreach($p->prod_feat_imgs as $feat){ $icon = $pages->get("$feat->prod_featicon_pages"); if($icon->image) { if($feat->prod_feat_textlang) { $icontitle = $feat->prod_feat_textlang;} else {$icontitle = $icon->title;} $out .= "<img src='".$icon->image->size(35,35,$imgOptions)->url . "' alt='" . $icontitle . "' title='" . $icontitle . "' class='listing-feat-icon' style='margin-right: .5em;' />"; } } $out .= "</div>"; if($p->prod_product_line){ foreach($p->prod_product_line as $pline) if($pline->image) { $out .= "<div style='height: 35px;'>\n"; $out .= "<img src='{$pline->image->size(75,35,$imgOptions)->url}' alt='{$pline->title}' />"; $out .= "</div>"; } } } $out .= "</div>"; $out .="</div>"; } $out .= "</div>";  
      Is there a clear culprit here of what I'm doing that's so stressing the system?
      I turned off TracyDebugger because I saw another thread about that causing slowdown (even though I'm using the latest), but that had no effect. Every time I thought I found the culprit and commented it out, nothing changed.
      Would appreciate some more eyes on this. Thank you!
      ETA: prod_feat_imgs is a repeater field which contains a Page reference field (from which I pull the image and title) and a multilanguage textfield (to override the page reference title if it exists). Could that be the problem?
    • By Mirza
      I have built a system in processwire, which has more than 600K pages.
      A team of 40 people is using the system, DB is from AWS with 16GB Ram.
      But still, select queries are getting locked.
      It would be great if someone suggests how to solve this problem.
      Also note: We have around 48 fields in one template.
      Thanks in advance.


    • By rastographics
      With Microsoft Azure recently releasing proper Linux and native Mysql hosting, it is becoming very easy to get processwire hosted on that platform.
      By default, they force SSL connection to the managed Mysql server. This causes processwire to fail when trying to connect.
      I can turn off SSL for my MySql server, but I wish there was a $config setting like $config->useSSL = true that would allow processwire to include the appropriate property in the connection string (like ssl=true or whatever).
      Here is the Azure link that explains what I would like to do: https://docs.microsoft.com/en-us/azure/mysql/concepts-ssl-connection-security
      Am I missing this config setting somewhere? Or would it be easy to add? Thanks.
    • By Brian Scramlin
      Hey!
      Quick question, 
      Should I limit how many find() methods I use on a single page? Is it fairly resource-intensive?
      I am getting consistent 
      2006 MySQL server has gone away errors and have followed all the suggestions I can find, but wonder if I am just "overloading" my server resources with database requests or something like that?
      Thank you!
    • By kixe
      Today I have been running in mysql errors using @renobird s Module MarkupActivityLog which still uses mysqli Driver.
      @all developers
      Although mysqli is still supported PDO driver is the default database driver in PW since https://processwire.com/about/news/introducing-processwire-2.4/
      and its strongly recommend to all module authors to use/change-to PDO driver instead of mysqli.

      @renobird I have sent a pull request.
      A list of other affected modules not updated until now.
      (I try to keep them up to date. Please help)
      @apeisa ProcessTrashman, ProcessRedirects
      @netcarver ProcessDiagnostics