Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 01/16/2023 in all areas

  1. In case you are looking for a nice and easy replacement for Pages2PDF as you might face problems with it due to PHP 8 - this might help you to get started and helped me today to get a site up and running again. Thanks to @bernhard for this module. <?php namespace ProcessWire; // file: templates/invoice.php $pdf = $modules->get('RockPdf'); $pdf->settings([ 'mode' => 'utf-8', 'format' => [210, 297], 'img_dpi' => 300, 'default_font' => 'DejaVuSans', ]); // Pseudo header $pdf->write(wireRenderFile('pages2pdf/_header.php')); // Footer $pdf->set('SetHTMLFooter', wireRenderFile('pages2pdf/_footer.php')); // Body content $pdf->write(wireRenderFile('pages2pdf/invoice.php')); // Styleshee $stylesheet = file_get_contents('pages2pdf/pdf.css'); $pdf->write("<style>$stylesheet</style>"); // Actions $pdf->show(); In this example I re-use my already existing pages2pdf-templates and even CSS. It's a super simple PDF, so I really don't have to deal with page margins and such in this case. So you might want to read the docs of mPDF and RockPDF to get this working in your project. There is a lot more you can do in terms of settings and such which can be found in this post for example:
    2 points
  2. TagsToFolders Github: https://github.com/eprcstudio/TagsToFolders Modules directory: https://processwire.com/modules/tags-to-folders/ This is a small helper tool to visually organise fields/templates into folders in the menu when they are tagged. This is a simple module that can help you declutter your templates/fields menu by organising them in folders using tags. I initially envisionned this module to be used for cases where fields and/or templates were created by modules and thus not polute the ones created by the user.
    1 point
  3. Nice work @bernhard, I'm gonna try RockMigrations soon.
    1 point
  4. Just a bit of feedback: Tested the latest version on two different sites with both template sets I work with and so far no issue or unexpected behaviour. Awesome. Thank you!
    1 point
  5. Thanks everyone. I tried mailjet with smtp and it seems to work great!
    1 point
  6. No, conditional hooks are great. I just tried to show that this: Pages(template=foo)::saved is wrong and this is the correct version: Pages::saved(template=foo) See https://processwire.com/talk/topic/18037-2-date-fields-how-to-ensure-the-second-date-is-higher/#comment-158164 and
    1 point
  7. Here's one of my latest projects: https://petibol.pt/ Petibol develops and produces of all types of EPP (Expanded Polypropylene) and EPS components and packaging for various industries. This website is a collaboration between Supertiny and GOdesign. Super simple approach: The frontend is just SCSS and vanilla js "components" (no libraries), and pages are built with a blocks system based on a repeater field. Having tried a bunch of stuff between building this site almost a year ago and publishing it (Tailwind, AlpineJS, VUE...) it's pleasing to return to this site's code and compare the approach. Here I've basically set up Laravel Mix to compile SCSS, join and minify a bunch of <1kb js files. A BEM style approach to the styles so that I have a bunch of preset variables for typography, spacing and whatnot, and the JS files follow the same logic of identifying components like the hamburger or the parallax effect by looking for specific data-attributes and going from there. Super clean, performant, and couldn't be easier to pick up and maintain.
    1 point
  8. Hello all, I wanted to share a proof of concept approach on how we can have an infinite scroll pattern without writing Javascript. We can achieve this with htmx. What you will get An overview page with a list of posts (PW pages). On initial page load 5 posts will be shown. When scrolling down and reaching the last post, another 5 posts will be loaded via AJAX in the background and appended after the last post until no more pages are found. Prerequisites You are using the delayed output strategy, with a _main.php appended. Just like using the default site profile when installing ProcessWire You are using markup regions, in my _main.php I have a div#content that will be used for the output of the posts Inside site/config.php $config->useMarkupRegions = true; Inside site/templates/_main.php <!-- main content --> <div id='content'> </div> For script loading I am using a custom $config->bodyScripts FilenameArray Inside site/config.php $config->bodyScripts = new FilenameArray(); Inside site/templates/_main.php before the closing </body> tag <?php foreach ($config->bodyScripts as $file) : ?> <script src="<?= $file ?>"></script> <?php endforeach ?> </body> PW page structure for this tutorial In the page tree I have a parent page "Posts" with template "posts". All child pages of that page have template "post" In the template "posts" settings in the "URLs" tab, check "Allow Page Numbers" and save. Needed for pagination. When viewing the page "Posts" all logic happens inside site/templates/posts.php site/templates/posts.php <?php namespace ProcessWire; // posts.php template file // add htmx js from site/templates/scripts $config->bodyScripts->add($config->urls->templates . 'scripts/htmx.min.js'); $limit = 5; $posts = $pages->find("template=post, limit={$limit}"); $lastPost = $posts->last(); $nextPageUrl = $page->url . $input->pageNumStr((int) $input->pageNum() + 1); $hxAttributes = array(); $hxAttributes[] = 'hx-get="' . $nextPageUrl . '"'; $hxAttributes[] = 'hx-trigger="revealed"'; $hxAttributes[] = 'hx-swap="afterend"'; ?> <?php if (!$config->ajax) : ?> <section pw-append="content" class="posts" hx-headers='{"X-Requested-With": "XMLHttpRequest"}'> <?php endif ?> <?php foreach ($posts as $post) : ?> <article class="post" <?php if ($post == $lastPost) echo implode(' ', $hxAttributes) ?>> <header> <h3><?= $post->title ?></h3> </header> </article> <?php endforeach ?> <?php if ($config->ajax) return $this->halt() ?> <?php if (!$config->ajax) : ?> </section> <?php endif ?> And that is all there is to it. Not a single line of Javascript, thanks to htmx. I followed the infinite scroll pattern from the official htmx examples. Now let's break the code down into easily digestible chunks // add htmx js from site/templates/scripts $config->bodyScripts->add($config->urls->templates . 'scripts/htmx.min.js'); This adds site/templates/scripts/htmx.min.js to our custom $config->bodyScripts FilenameArray so it will be loaded in _main.php. You can get the script here from unpkg.com. $limit = 5; $posts = $pages->find("template=post, limit={$limit}"); Sets our pagination limit to 5 and loads the correct set of posts. $lastPost = $posts->last(); Saves the last post of each set. We use this later to determine whether the htmx attributes should be rendered. $nextPageUrl = $page->url . $input->pageNumStr((int) $input->pageNum() + 1); We are building the link to the next "page" with the next set of posts. Will result in something like "/posts/page2", "/posts/page3" etc. $hxAttributes = array(); $hxAttributes[] = 'hx-get="' . $nextPageUrl . '"'; $hxAttributes[] = 'hx-trigger="revealed"'; $hxAttributes[] = 'hx-swap="afterend"'; Define our htmx attributes as an array. They will be added to every last post's HTML. Note the hx-get attribute which will be the URL for the AJAX call in the background. That request is triggered whenever the last post becomes visible inside the viewport while scrolling down. hx-swap afterend tells htmx to append the next batch of posts after the last post. <?php if (!$config->ajax) : ?> <section pw-append="content" class="posts" hx-headers='{"X-Requested-With": "XMLHttpRequest"}'> <?php endif ?> // and <?php if (!$config->ajax) : ?> </section> <?php endif ?> Renders the wrapping section tag only on initial page load which is a none AJAX request. Note the hx-headers='{"X-Requested-With": "XMLHttpRequest"}'. This adds an additional header to all AJAX requests with htmx. We need this header so ProcessWire understands that it is an AJAX request. Otherwise $config->ajax would always return false. See https://htmx.org/attributes/hx-headers/ for more info <?php foreach ($posts as $post) : ?> <article class="post" <?php if ($post == $lastPost) echo implode(' ', $hxAttributes) ?>> <header> <h3><?= $post->title ?></h3> </header> </article> <?php endforeach ?> Render each posts's HTML. If it is the last post, also render our htmx attributes. For brevity in this example I only output the post title. <?php if ($config->ajax) return $this->halt() ?> For AJAX requests stop execution of the template file and everything that follows. This prevents appending of _main.php for ajax calls. So we only get the desired HTML for the list of posts and no head, footer etc. Summary Compared to other approaches, htmx lets us control all our AJAX logic with a few html attributes. Really neat and concise. Easypeasy. I like that and will surely use an approach like this in future when infinite scroll is needed. What I like in particular is how easy this is implemented with ProcessWire's powerful pagination capabilities. If you have the same page structure, the code in site/templates/posts.php is working out of the box as is. I have this running on a standard PW multilang site profile with additions/amendments mentioned above under "Prerequisites". Here's a visual of the result:
    1 point
  9. @ryan - I think the biggest issue I am seeing is the actual autosave - that's what is causing the 500 error when making the AJAX call. If I the type in buildAjaxRequest() to GET, then I don't get the error any more. It's still not autosaving, but that's not surprising given that ProcessPageEdit is expect GET, but maybe it gives you a clue as to why I might be getting the error. This is what debug is returning: [ "submit_autosave=1", "id=7689", "body=%3Cp%3Ehjhdh+dhjdk+dhjdkg+dfghdf%26nbsp%3B%3C%2Fp%3E%0D%0A", "TOKEN643048379X1637275316=pObPhEnWs6iGOEdu.4kZzL2ATScfNNl0" ] { "type": "POST", "url": "./?id=7689&modified=1637275019&fields=body", "data": "submit_autosave=1&id=7689&body=%3Cp%3Ehjhdh+dhjdk+dhjdkg+dfghdf%26nbsp%3B%3C%2Fp%3E%0D%0A&TOKEN643048379X1637275316=pObPhEnWs6iGOEdu.4kZzL2ATScfNNl0", "dataType": "json" } Autosave error: ajax error: error (body)
    1 point
  10. Thanks. That works! I was playing around a bit more with it, and during that I managed to completely trash Tracy (it was gone all of a sudden), had massive problems with PW's file compiler and Tracy Cache, plus PHP memory limits and whatnot... Most probably not related to mPDF or RockPDF at all, I guess I was using wireRenderFile() wrong... Here's a little example (generates a 24-pages PDF with limited rights, meta-data, and custom font in approx. 3 seconds while in debug mode) <? @ini_set('memory_limit', '8192M'); // just to be on the safe side @set_time_limit(600); // ditto $pdf = $modules->get('RockPdf'); $pdf->settings([ 'mode' => 'utf-8', 'defaultheaderfontsize' => 9, 'defaultheaderfontstyle' => 'I', 'defaultheaderline' => 0, 'SHYlang' => 'de', // doesn't work 'hyphens' => 'auto', // doesn't work - I have this also in my CSS 'margin-top' => 20, // doesn't seem to work either, for some reason 'fontdata' => [ "inter" => [ 'R' => "Inter-Regular.ttf", 'B' => "Inter-Black.ttf", 'I' => "Inter-Italic.ttf", 'BI' => "Inter-BlackItalic.ttf", ]], 'default_font' => 'inter' ]); $pdf->set('SetHeader', 'Offerte Steuerrechner Kanton Zürich 2019, Test AG, ' . date('H:i:s')); $pdf->set('SetFooter', '{PAGENO}'); $pdf->set('SetTitle', 'Offerte Steuerrechner Kanton Zürich 2019'); $pdf->set('SetAuthor', 'Me Myself and I'); $pdf->set('SetCreator', 'Test AG'); $pdf->set('SetSubject', 'Offerte Steuerrechner'); $pdf->set('SetKeywords', 'Steuerrechner, Offerte, Angebot, Test AG, Frontend-Entwicklung'); $pdf->set('SetProtection', array('copy', 'print', 'print-highres')); $myOffer = 11568; // each offer has a parent page with meta-data, and "chapters" as children. We pass this to the template on the next line $str = wireRenderFile("/home/mysitecom/www/dev.test/site/templates/offer-index-dev.php", array("offer_index_page_id" => $myOffer)); $fn = $pages->get($myOffer)->name; // will be used to generate filename.pdf $pdf->write($str); d($pdf->save("/home/mysitecom/www/dev.test/site/assets/files/$myOffer/$fn.pdf")); // generate pdf in PW default file assets folder // in offer-index-dev.php, I have this at the top if(!isset($offer_index_page_id)) { return $this->halt(); exit(); } if(isset($offer_index_page_id)) { $offer_index = $pages->get($offer_index_page_id); } // I then use $offer_index->title etc. instead of $page->title further below Not exactly rocket science, but maybe somebody else can use bits of it as first baby-steps ?
    1 point
×
×
  • Create New...