Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 02/20/2021 in all areas

  1. This week I've been developing a client site, but also working through some of the feature requests. I'm going through them in order of "thumbs-up" on GitHub and trying to focus more on those that I can get through reasonably quickly. For a summary of what's been added, be sure to see the dev branch commit log. There's still a few more I'd like to add before bumping the version to 3.0.173, so will save that for next week. One of the requests was for the ability to add custom after-save actions in the Page editor. These are the "Save + Exit", "Save + Add New", etc., dropdown actions that you see on the Save button in the page editor. This is something that's already supported, but not formally documented. So I wanted to quickly go through a couple examples of how to do that here, as it is kind of useful and fun. Let's start with a "hello world" example to keep it simple, then we'll move on to a more practical example. Say we want a "Save + Say Hello" dropdown action in our page editor Save button. We need one hook to add the action, and another to process it. These hooks could go in your /site/ready.php file or your /site/templates/admin.php file, or in a module. First we'll want to hook ProcessPageEdit::getSubmitActions() to add our custom "hello" action: $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array of actions indexed by name $page = $event->object->getPage(); // page being edited // add a new action that says hello after saving page $actions['hello'] = [ 'value' => 'hello', // value for action that you will check 'icon' => 'hand-spock-o', // icon to show in action, excluding the 'fa-' part 'label' => '%s + Say Hello', // the '%' is replaced with 'Save' or 'Publish' 'class' => '', // optional class if you need different styling for button/link ]; $event->return = $actions; }); To process our action, we'll need to add a hook to ProcessPageEdit::processSubmitAction(): $wire->addHookAfter('ProcessPageEdit::processSubmitAction', function($event) { $action = $event->arguments(0); // action name, i.e. 'hello' $page = $process->getPage(); // Page that was edited/saved if($action === 'hello') { $notice = new NoticeWarning("Hello! You edited page $page->path"); $notice->icon = 'hand-spock-o'; $event->notices->add($notice); } }); That's all there is to it. That part where I created the $notice above could just as easily been $this->warning("Hello!..."); but I wanted to add a custom icon to it, which is why I created the Notice manually. Many of the built-in after-save actions perform a redirect to another location, such as adding another page, exiting back to the page list, viewing the page on the front-end, etc. If you have a need to perform a redirect after the save, use the $event->object->setRedirectUrl($url); method. This is preferable to calling $session->redirect(); yourself, as it is handled by the page editor after it has finished everything it needs to do. What if you just want to remove one of the existing actions? For instance, maybe you want to remove the "Save + Add New" action. That action has the name "add", so we can remove it like this: $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array of actions, indexed by name unset($actions['add']); $event->return = $actions; }); If there's another you'd like to remove, I'd recommend using TracyDebugger and bd($actions); so you can see and identify all the actions that are present from your hook. Now let's get to a more practical example. Let's say that you are using ProCache and you want to add a save action to automatically prime the cache after performing the save. By "prime the cache" I mean have it perform a page-view on the front-end that makes it save a new cache file for the page. Here's how you could do that: $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array $page = $event->object->getPage(); // page being edited $procache = $event->wire('procache'); if(!$procache) return; // if procache not installed, do not add action if(!$page->isPublic()) return; // page not public is also not cacheable if(!$page->viewable()) return; // page not viewable has no template file if(!$procache->isPageCacheable($page)) return; // page not setup for cacheing $actions['cache'] = [ 'value' => 'cache', 'icon' => 'fighter-jet', 'label' => '%s + Cache', // Save + Cache 'class' => '', ]; $event->return = $actions; }); …and our hook to process the action: $wire->addHookAfter('ProcessPageEdit::processSubmitAction', function($event) { $action = $event->arguments(0); // action name, i.e. 'cache' $page = $process->getPage(); // Page that was edited/saved if($action === 'cache') { $http = new WireHttp(); $response = $http->get($page->httpUrl); if($response) { $size = wireBytesStr(strlen($response)); $event->message("Cache primed for page $page->path ($size)", "nogroup"); } else { $this->warning("Error caching page: " . $http->getError()); } } }); Note that we don't have to clear the cache file here because that's something that ProCache has already done prior to our hook above being called. Thanks for reading and have a great weekend!
    13 points
  2. Long time no post. Here's my latest: https://maisliberdade.pt/ Mais Liberdade is a liberal think-tank in Portugal, promoting the values of liberal-democracy, individual freedom and free market economy. It's a non profit that gathers collaboration from people from multiple portuguese political parties, members of the european parliament, economists, professors, etc. During development, an announcement page was set up with a registration form for founding members. In that period of about a month, around 200 subscriptions were expected, but in the end we got over 6000 subscribers. This website features essays, events, videos and a free library that, at the time of this post is counting 400 books. The frontend was built using web components (Stencil js). Basic pages are built with a modular approach, I'm attaching a video of how they are created. The approach is a simple repeater with a custom block type selector interface. That selector is basically a modified version of FieldtypeSelectFile. I've hacked that module to expect a PNG to be available for each PHP file in the blocks folder, and modified the input field to generate a button grid after the select. This is lovely to use from the editor's perspective, but it's something to improve for the developer experience, because in the end this is a repeater, that has to contain all fields that each type of block needs, and for each of those fields I have to set it to show only if the blockType field equals X, Y or Z. With a lot of different block types this takes some planning and easily becomes hard to manage, but it's the best approach I found yet and the benefit for the editor's experience is well worth it. covered-compressed.mp4
    6 points
  3. From the recent discussion about the roadmap & wishlist for 2021 and some other posts by @ryan, it comes to my mind that developing and coordinating the whole project for one person is becoming harder and harder and leads nearly to the reverse of expanding the ProcessWire ecoysystem. This is not against Ryan, i think everyone here knows how engaged he is about ProcessWire, but he has only 24/7 (sometimes i think he's got far more than that...). We as the community could support the project (financially) to relieve Ryan and could take over some tasks from him. This could be, but is not limited, to: Building a Foundation/Association/Company to ensure the persistence of the project and to fund the work put in ProcessWire of Ryan (and others). Nearly every other CMSs i checked has something like this (Drupal Association, Typo3 Association, Joomla Foundation, Wordpress Foundation, Contao Association, ...). This also puts more trust in the project, if someone new will check on his engagement in ProcessWire. Assigning persons/teams to work on things: Extending the core (when necessary) Developing and maintaining major modules (e.g. page builder, admin themes, internationalization, marketing, ecommerce system, ...) Testing and inspection of modules developed by others Making translations of modules (translation of the core is mostly covered, i think) Working on PRs & issues submitted on github Working on the homepage Coordinating the community efforts I know, some resorts are already covered by others (e.g. @Pete for the forum, @horst for images, ... ), but there are many other areas where this ist not the case. By joined efforts by the ProcessWire community this hopefully will also attract new developers to the system and by a growing number of users this assists in the things above in a circular process. What do you think?
    3 points
  4. If done correctly I think that could be a great plus for ProcessWire! And I'd be happy to be part of such an association ?
    3 points
  5. A couple weeks ago there were a lot of ideas about page builders and different approaches in ProcessWire. Some really interesting ideas actually, and great proof of concepts as well. The conversation started moving faster than I could keep up with, and I think Teppo's summary in ProcessWire Weekly 351 is a great overview, if you haven't yet seen it. These are so great in fact, that I don't think anything I build could live up to what some of you have already built. In part because my focus is more server-side than client-side development, and I can clearly see most of these page builders are much more client-side based than I have expertise in (though would like to learn). Whereas some of you are clearly amazing client-side developers; I'm blown away by what's been built, and how quickly it happened. One struggle I have with page builders is that I don't have my own good use cases for them, with the projects that I work on. Others here (like Jonathan) already have a lot of good use cases. Clearly there are a lot of benefits. I'm fascinated and enthusiastic by all of it, but can't envision how or if I'd use such a tool in my own projects. And having experience with (and particularly close use cases for) a tool is important in hitting the right notes in building it. Likely page builder support belongs in ProcessWire in some form or another, and ProcessWire can be an excellent platform for it. But the actual page builder needs to be designed and built by the people that have expertise in the tools needed to build it, and by the people that will actually use it. I think it should be a community collaboration. For anything needed to support this on the PHP side, count me in. I don't think I've got the background to "lead" the development of the page builder, but I do think I've got plenty to contribute to it. I'd also like to continue building out existing tools like Repeater/Matrix to further support page builder type use cases. I'd like to rebuild and move the auto-save and Live Preview features from ProDrafts into the core, which should also help support some of these page builder cases. Whether these will eventually apply to the larger page-builder project, I don't know, but they'll be useful either way. What are the next steps? Who's interested in working on this? Let's keep the conversion going and move forward with it.
    1 point
  6. try putting this somewhere like config.php or something. setlocale(LC_ALL, 'de_DE.utf8');
    1 point
  7. I’m not certain I correctly understood the problem, but in general it will save you some headaches if you set up the page tree according to the canonical urls you want. What should be the canonical urls for specific books and specific categories? If you want a The Hobbit’s $page->url to be /books/the-hobbit and the fantasy section’s to be /books/fantasy, go with the mixing! If you want every book to have sort of a primary category, put each book under a category. Then it’ll be /books/fantasy/the-hobbit. You can still have /books/the-hobbit by using urlSegments, but you’ll obviously have to watch out for books that have the same page name as a category. And you will have to generate those links yourself somehow. I suppose you should also ask yourself if you really need the same page to be accessible by different urls?
    1 point
  8. You might be interested in the new “preload module“ feature: https://github.com/processwire/processwire/commit/c4d301a40530cfa3b00ce559a14757684625a5ce But you still won’t get around what Zeka said, I guess. Can’t you just take the time in your module’s init()/__construct()? That should be the earliest opportunity for a module to do anything anyway.
    1 point
  9. put this in your head tag <meta http-equiv="content-type" content="text/html; charset=utf-8" /> and make sure your PHP files are UTF8. VS Code tells you in the status bar. Notepad++ has “Encoding” in the menu bar. Make sure it isn’t UTF8BOM. Is $all_events the contents of a FieldtypeTable? How much stuff is there to iterate? If you fetch a large amount of data just do discard it, it might be worthwhile to move the filtering to the database. Also if you just want all events that interesect the year somehow, you can do this foreach ($all_events as $ae) if ($ae->date_start <= $year_end && $ae->date_end >= $year_start && $ae->date_end>=$ae->date_start) $year_overview->add($ae);
    1 point
  10. I third that. It can be very helpful for ProcessWire long term development. Gideon
    1 point
  11. Sure. In fact, I was wrong to say that it works fine - I needed to make a small adjustment, which also enables more checking. The page object in the hook has the admin template - so we need to get the actual page being edited in order to get the override field. $this->addHookAfter('Page::render', function(HookEvent $event) { $p = $event->object; if ($p->template == 'admin') { $pId = wire()->input->get->int('id'); $page = wire('pages')->get($pId); if ($page and $page->template == 'BookingForm') { $event->return = str_replace("</body>", '<input type="hidden" id="Inputfield_override" name="override" value="' . $page->override . '"></body>', $event->return); } } });
    1 point
  12. Hi @Clément Lambelet I haven't tried to hook to ProcessWire::init within module's init method, but probably it's too late to declare hook for init method as modules initialization method is triggered within this method, have you tried to add your hooks in initBefore.php or boot.php? ( You can create these additional status files within 'site' directory and set them in your config file). https://github.com/processwire/processwire/blob/master/wire/config.php#L1559
    1 point
  13. Make sure you limit it to the PageEditor interface in the admin - you don't want that checkbox appended everywhere back and frontend ?
    1 point
  14. Hi @MarkE Sorry I've been so long to respond - way too busy here at the moment. I don't have a fully working solution for you (I couldn't quite get there as quickly as I need to), but here are some starting points. The first one is hacky. The second is where you want to end up, but you'll probably want to do a $f->addClass('hidden') or something like that. I also didn't want to use a Markup inputfield but it seemed like there were issues with overriding the main "override" field. Also, using $p->override isn't actually working because it's preventing you from changing the value that is already in the system but if you set it to 0 or 1, you'll see that the approach has merit in that it will change the visibility of your other field. Sorry it's not fully working, but hopefully gets you there. Remember the key thing is that showIf conditions work via JS, so the values need to be available that way, not just via $page->override like you were trying. $this->addHookAfter('Page::render', function(HookEvent $event) { $p = $event->object; $event->return = str_replace("</body>", '<input type="hidden" id="Inputfield_override" name="override" value="'.$p->override.'"></body>', $event->return); }); $this->addHookAfter("ProcessPageEdit::buildForm", function(HookEvent $event) { $p = $event->object->getPage(); bd($p->override); $form = $event->return; $f = $this->modules->get("InputfieldMarkup"); $f->attr('id+name', 'override'); $f->attr('value', '<input type="hidden" id="Inputfield_override" name="override" value="'.$p->override.'">'); //$f->collapsed = Inputfield::collapsedHidden; $form->insertAfter($f, $form->title); $event->return = $form; });
    1 point
  15. Thanks for the report @adrian. The newly released v0.3.6 adds support for ProcessProfile and ProcessUser, and also adds a config field to show the Tracy debug bar in the dialog which can be helpful for debugging if you are building the dialog form with a hook.
    1 point
  16. Both. It causes the inputfield to be rendered via renderValue() so there's no input, and even if somebody manually added an input in their browser any processing of the inputfield is prevented.
    1 point
  17. Thanks for another great module @bernhard Wanted to share my experiences changing from Pages2PDF to RockPdf. Wasn't quite as straightforward as I'd hoped. Such is life, but I got there. Pages2Pdf uses mPDFv5+ while RockPdf uses mPDFv7+ and I needed some of the newer features. Things Iearned along the way: 1. If you need to use @page, you lose everything in the template $pdf->set('header', '...'); settings. This has nothing to do with RockPdf but a 'feature' of mPDF 2. It's much easier to add custom fonts to RockPdf than Pages2PDF 3. You can display images in a circle using background-image but they don't print so that's not helpful, LOL This is NOT a full tutorial but hopefully will give you some pointers on how I got RockPdf working for a fairly specific PDF design. RockPdf template settings: <?php $pdf = $modules->get('RockPdf'); $pdf->settings([ 'fontdata' => (new \Mpdf\Config\FontVariables())->getDefaults()['fontdata'] + [ "montserrat" => [ 'R' => "montserrat-regular.ttf", 'B' => "montserrat-bold.ttf", ], "montserratlight" =>[ 'R' => "montserrat-light.ttf" ], "montserratthin" => [ 'R' => "montserrat-thin.ttf" ] ], 'defaultheaderline' => 0, 'font_size' => 9, 'mode' => 'utf-8', 'font' => 'montserrat', 'page_format' => 'A4', ]); $css = wireRenderFile($config->paths->templates . 'RockPdf/styles-v3.css'); $pdf->write("<style>" . $css . "</style>"); $body = wireRenderFile($config->paths->templates . 'RockPdf/profile_pdf_cv-v3.php'); $pdf->write($body); $pdfFile = $sanitizer->pageName($profile->title) . "-" . $profile->id . ".pdf"; $pdf->show($pdfFile); die(); // Remove old Pages2PDF settings // $mpdf->markupMain = $config->paths->templates . 'RockPdf/profile_pdf_cv-v3.php'; // $mpdf->markupHeader = $config->paths->templates . 'RockPdf/_header-v3.php'; // $mpdf->markupFooter = $config->paths->templates . 'pages2pdf/_footer-v2.php'; // $mpdf->cssFile = $config->paths->templates . 'RockPdf/styles-v3.css'; // $mpdf->pageOrientation = 'P'; // $mpdf->pageFormat = 'A4'; // $mpdf->topMargin = 9.5; // $mpdf->rightMargin = 0; // $mpdf->bottomMargin = 9; // $mpdf->leftMargin = 0; // $mpdf->headerMargin = 0; // $mpdf->footerMargin = 0; // $mpdf->fontSize = 9; // $mpdf->mode = 's'; // $mpdf->font = 'montserrat'; Header code: <div style=" background-color: #007ee5; height: 10mm; margin: 0; top: 0; left: 0; right: 0; width: 100%; "> </div> Page layout code: <?php // header is the same on all pages but need more spacing on all pages except the first $header = wireRenderFile($config->paths->templates . 'RockPdf/_header-v3.php'); ?> <htmlpageheader name="myHeaderFirst" style="display:none"> <?=$header?> </htmlpageheader> <htmlpageheader name="myHeader" style="display:none"> <?=$header?> </htmlpageheader> <sethtmlpageheader name="myHeaderFirst" value="on" show-this-page="1" /> <sethtmlpageheader name="myHeader" value="on" /> <div class="user-dets"> CSS: /* Additional fonts added to: site/assets/RockPdf/fonts */ @page { margin: 15mm 0 0 0; /* <any of the usual CSS values for margins> */ /*(% of page-box width for LR, of height for TB) */ margin-header: 0; /* <any of the usual CSS values for margins> */ margin-footer: 9mm; /* <any of the usual CSS values for margins> */ marks: none;/*crop | cross | none*/ header: html_myHeader; } @page :first { margin: 9.5mm 0 0 0; /* <any of the usual CSS values for margins> */ /*(% of page-box width for LR, of height for TB) */ margin-header: 0; /* <any of the usual CSS values for margins> */ margin-footer: 9mm; /* <any of the usual CSS values for margins> */ marks: none;/*crop | cross | none*/ header: html_myHeaderFirst; } Hope this is useful. Cheers psy
    1 point
  18. Yes it is good and in use on the site https://www.p-jentschura.com/ I added this directly in the HTML so it has nothing to do with ProcessWire in general, but I am thinking about developing a module for it to set the options. But thats only for the far future and next website project.
    1 point
×
×
  • Create New...