-
Posts
6,638 -
Joined
-
Last visited
-
Days Won
360
Everything posted by bernhard
-
@Jonathan Lahijani thank you very much! A lot of what you wrote resonates with me. Did you also try/consider daisyui? The have 35k stars on Github, a MIT license and so far I didn't find anything that I would miss coming from UIkit.
-
Just found https://preline.co/docs/index.html today and I'm quite impressed! It looks like a more modern UIkit ๐ @Jonathan Lahijani did you find time to try it since 2022 and have some experiences to share with us?
-
Hey @erikvanberkum what breaking changes are you talking about? MagicPages have been there for ages and the latest breaking change was really only about removing the hideFromGuests feature which should have nothing to do with the MagicPages feature. It sounds like your system is loading RockMigrations multiple times somehow? Do I understand you correctly that this is a fresh installation of RockCommerce + RockForms + all dependencies? Not an existing site? No updates or anything else?
-
@ryan any feedback on my related suggestion? https://processwire.com/talk/topic/31404-weekly-update-โ 8-august-2025/?do=findComment&comment=249930 What I forgot to mention: For that to work and to make it secure we'd have two options: Add a secret token to the users module directory profile that anybody can use to send update info to the modules directory Use a push-pull concept. The user just triggers and endpoint on processwire.com (like processwire.com/update-module/?module-id=1234) and then PW calls a predefined endpoint to grab data from (eg baumrock.com/get-module-info/?pw-module-id=1234) This endpoint would have to be defined in the module's directory of the user so that anybody can use their own endpoint. I'd prefer (1) as this would be a one-time setup on your end and anybody can just copy and paste that token and add it to the request payload for the trigger, eg: { "token": "abcdefg", "module-id": "1234", "version": "1.2.3", "readme": "This is a calendar module for PW" } And I think (1) would even be easier to implement on your end as well ๐
-
@elabx you might want to use this approach and create a dynamic runtime superuser: https://github.com/baumrock/RockShell/blob/64a453a518e11dc445ae451bf1ca2d5e30126ef6/App/Command.php#L588-L590 $su = new User(); $su->addRole("superuser"); wire()->users->setCurrentUser($su);
-
I would prefer to give it more real world testing before I do that. In my projects I seem to get some blocking behaviour if a stream is running. It has not been an issue with the sse based livereload but I'm working on an sse based page edit lock and I'm seeing some strange behaviour unfortunately.
-
@poljpocket so you think the current solution for "empty trash" where after clicking it the user gets no feedback at all about what is going on and about the progress is a good user experience? And then if the chunk was not enough you have to click again and again until the trash is empty... And the solution for that is to install the Sse module from baumrock's github profile?
-
Nice!! Yeah, absolutely ๐
-
From my research/testing this was necessary to make the SSE stream non-blocking. When using regular url hooks after the session has been started, then you can't open any other tabs while the stream runs. That's one of the caveats that you'll have to fight when developing something from scratch. Thx ๐ For example?
-
I've built this over and over in several of my modules (RockDevTools for Livereload, RockCalendar for creating events). Always a lot of work. Always a lot of boilerplate code. Always a lot of issues to fix. I think it's time to bring SSE (Server Sent Events) to everybody. Simple example (Empty Trash): Client-Side: // create stream const stream = ProcessWire.Sse.stream( 'ssedemo-empty-trash', (event) => { const textarea = document.querySelector('textarea[name="empty-trash-status"]'); try { let json = JSON.parse(event.data); stream.prepend(textarea, json.message, 100); } catch (error) { stream.prepend(textarea, event.data); } } ); // update progress bar const progressBar = document.querySelector('#empty-trash-progress'); stream.onProgress((progress) => { progressBar.value = progress.percent; }); // click on start button document.querySelector('#empty-trash').addEventListener( 'click', (e) => { e.preventDefault(); stream.start(); }); // click on stop button document.querySelector('#stop-empty-trash').addEventListener( 'click', (e) => { e.preventDefault(); stream.stop(); }); Server-Side: public function __construct() { parent::__construct(); /** @var Sse $sse */ $sse = wire()->modules->get('Sse'); $sse->addStream('ssedemo-empty-trash', $this, 'emptyTrash'); } public function emptyTrash(Sse $sse, Iterator $iterator) { $user = wire()->user; if (!$user->isSuperuser()) die('no access'); $selector = [ 'parent' => wire()->config->trashPageID, 'include' => 'all', ]; // first run if ($iterator->num === 1) { $iterator->max = wire()->pages->count($selector); } // trash one page at a time $p = wire()->pages->get($selector); if ($p->id) $p->delete(true); else { $sse->send('No more pages to delete'); return $sse->stop(); } // send message and progress info $sse->send( $iterator->num . '/' . $iterator->max . ': deleted ' . $p->name, $iterator ); // no sleep to instantly run next iteration $sse->sleep = 0; } Code + Readme: https://github.com/baumrock/SSE/tree/dev What do you think? Would be nice if you could test it in your environments and let me know if you find any issues!
- 10 replies
-
- 10
-
-
Congrats on the launch @ryan Would it be possible to get some kind of webhook that I can call and send data to? I have everything automated for all my modules and I really don't want to log into the modules directory to push all my updates manually ๐ It would probably be ok to add all the readmes once, but still all the version numbers and the "last updated" indicator would be outdated as all my paid modules are private git repos. And I put a lot of effort into all my pro modules, so it would be nice if that could be visible somehow in the modules directory. See RockCalendar as an example: https://www.baumrock.com/en/releases/rockcalendar/ Ideally we'd have a github workflow that anybody can add to his/her project that automatically sends new data to the modules directory on every new release. I can build that workflow if you want and all you'd have to do is to add it to the github repo (like this example from RockShell) so that we all can use it in our CI automations and then add an url hook to processwire.com that reads the data from the webhook and updates the modules directory entry.
-
JavaScript hooks are totally client side. There is no Server involved and also no PHP. Everything happens in JavaScript. The reason why they are extremely useful is because the developer of the JS module can easily make things customisable and we as devs or users can use hooks to change the code execution. Imagine someone builds a modal component for the core. We have a modal.show() and modal.hide() method. Without hooks, that's it. Maybe the developer was wise and added events to it that we can listen to. Maybe something like "modal:shown" or "modal:hidden". UIkit for example has these events: beforeshow Fires before an item is shown. show Fires after an item is shown. shown Fires after the item's show animation has been completed. beforehide Fires before an item is hidden. hide Fires after an item's hide animation has started. hidden Fires after an item is hidden. Now the problem: How would you, as a developer, modify the behaviour of the modal? For example how would you prevent showing the modal under certain circumstances? That's easy when having hooks. All we need is making ___show() and ___hide() hookable and we don't need those events any more, because we can hook into the modal's business logic: ProcessWire.addHookBefore('Modal::show') ProcessWire.addHookAfter('Modal::show') ProcessWire.addHookBefore('Modal::hide') ProcessWire.addHookAfter('Modal::hide') -------------- Another example is RockCommerce. The challenge here is not the backend - we all have the same backend. The challenge is the frontend, as every frontend is different. And every RC customer might need different pricing calculations or different features (like preventing to select items, max amount, custom tax calculations, coupons, etc...). All this can easily be done by providing hooks. RockCommerce does the heavy lifting and provides the base implementation and my customers can add modifications as they need. -------------- Another example could be a DatePicker. It often has some init call like this: const picker = new WhateverDatePicker({ firstWeekday: 0, okLabel: "Apply", ... }); This code will be somewhere in a core .js file. That means it is hardcoded! How would we make the "firstWeekday" setting customisable by the user? So that some can set Monday and some Sunday as first day of the week? And what about the okLabel? etc, etc... With hooks it is easy. The dev just adds a hookable method and all good: Class WhateverDatePicker { function init() { const picker = new WhateverDatePicker(this.getSettings()); } ___getSettings() { return { firstWeekday: 0, okLabel: "Apply", ... } } } Now anybody could easily hook into the setup of the Datepicker: ProcessWire.addHookAfter('WhateverDatePicker::getSettings', (event) => { let settings = event.return; settings.firstWeekday: 1; // change from 0 to 1 settings.okLabel: 'Anwenden'; });
-
RockDaemon - Easy Background Task Management for ProcessWire โ
bernhard replied to bernhard's topic in Modules/Plugins
Thx that was not intentional ๐ -
Random errors are the worst! ๐ If you have TracyDebugger installed already you might also want to look into the folder /site/assets/logs/tracy You might find some tracy bluescreen html files there which you could copy to your computer and open in a browser and see something like this: You can click on each item of the trace and you'll see where the problem occurred (top arrow) and also which arguments have been sent to the method (bottom arrow). Good luck ๐ค
-
RockDaemon simplifies running long-running background tasks (daemons) with automatic lifecycle management. Ideal for tasks like PDF generation, email processing, or data synchronization that need to run continuously via cron jobs. Why RockDaemon? Running long-running tasks in PHP can be challenging: Preventing multiple instances from running simultaneously Manual restart capabilities Automatic restart after deployments Signal handling and graceful shutdown Debug output control Command-line argument parsing Hosting environment compatibility RockDaemon solves these problems with a simple, cron-based approach. Example <?php // pdf-daemon.php namespace ProcessWire; use RockDaemon\Daemon; require_once __DIR__ . '/public/index.php'; $rockdaemon = wire()->modules->get('RockDaemon'); $daemon = $rockdaemon->new('pdf-daemon'); $daemon ->run(function (Daemon $daemon) { // get a newspaper page that has the "createPdf" checkbox enabled $p = wire()->pages->get([ 'template' => 'newspaper', 'createPdf' => 1, ]); if (!$p->id) return $daemon->echo('Nothing to do'); $timer = Debug::startTimer(); $p->createPdf(); $ms = Debug::stopTimer($timer) * 1000; $daemon->log( message: "created PDF for $p in {$ms}ms", logname: "pdf-create", pruneDays: 30, ); $daemon->run(); }); Docs + Download: baumrock.com/RockDaemon Showcase in PW Weekly: https://weekly.pw/issue/590/
- 2 replies
-
- 11
-
-
-
Needed the exact same thing today and came up with this solution that allows using regular page selectors: public function getSheetToCreatePdf(): SheetPage|NullPage { // make sure to only find pages that have been modified since creation wire()->addHookAfter("PageFinder::getQuery", function (HookEvent $event) { $query = $event->return; $query->where("pages.modified > pages.created"); $event->removeHook(null); }); // find the oldest page that has no PDF and make sure to flush the cache return wire()->pages->getFresh([ 'template' => 'sheet', 'pdf.count' => 0, 'has_parent' => '/papers', 'sort' => 'id', ]); } This is called from an endless reactPHP loop that automatically creates PDFs when new pages are modified ๐
-
Hy @poljpocket thx for your questions I'm not using the functions API myself either. I never use pages() in my module, for example, I'm always using wire()->pages->... which will work with $config->useFunctionsAPI = false; Sure. For me this is not an issue and I thought it would not be an issue for anybody else, because I didn't consider a staging site being visible to others on the same network being critical in any way. But you are right, it might not be the most secure approach, so I have added a config setting for this which is disabled by default. So the default will be to only allow access for the session and allow the IP only when the checkbox is checked. I added this because when I tested my site on staging with my mobile phone and got a signup confirmation via mail and clicked the activation link my phone opened that link in some other browser and therefore I got "access denied". Sure it would be possible to copy the link and open it in the same browser that is already authenticated, but explain that to clients that you sent the staging link for testing... ๐
-
[SOLVED] Feature request: a shouldRender() method for blocks
bernhard replied to FireWire's topic in RockPageBuilder
What I often do in such situations is to add this via hook: $wire->addHookMethod('Block::shouldRender', function($event) { $event->return = true; }); Then you have a shouldRender() method for all blocks that returns true by default and you can implement custom logic for each block that needs it. But I'm wondering... Maybe it would be a better idea to implement a DefaultBlock class that everybody can implement that RPB picks up and uses to extend every block on? You could then place your shouldRender() there and you could place anything else there as well. I'm a bit concerned about adding shouldRender() as it's really simple for your situation but if in the core I'm quite sure I'll get support requests why shouldRender() does not account for helper methods like getBlockNum() or isEvenType() etc... What do you think? I think having a DefaultBlock class would be nice and should help in your situation as well? -
[SOLVED] Feature request: a shouldRender() method for blocks
bernhard replied to FireWire's topic in RockPageBuilder
@FireWire not sure what you want to say or ask? -
@adrian unfortunately I'm lost with this and need some more help ๐ Where would I put this mapping and what would I place as patterns? It feels complicated to me and I'm a bit afraid of doing this for each and every project ๐ฎ I have a wrong return value in Editor.php on line 49 which throws a bluescreen like this: As you can see the path to that file shows /example.com/site/modules/.../Editor.php The tooltip shows the correct path in the DDEV web container: /public/site/modules... When I click the link it tries to open that folder in Cursor, but without the needed /public and therefore it does not find the file: I'm totally lost and I hope you have an idea?
-
[SOLVED] Feature request: a shouldRender() method for blocks
bernhard replied to FireWire's topic in RockPageBuilder
Hey @FireWire thx for the idea/suggestion! Sounds good to me! The only thing to consider would be to also account for hidden blocks. Blocks already have an _mxhidden flag/attribute which is used both for rendering/not rendering the block but it is also used in the helper methods like getBlockNum() or getBlockIndex(). The latter is the base implementation where it accounts for that flag. So maybe all we need to do is to move this: foreach ($items as $item) { if ($item->_mxhidden) continue; if ($item->_temp) continue; if ($item->id === $this->id) return $i; $i++; } Into a dedicated method as you suggest? -
[SOLVED] Compatibility with WireCache Filesystem
bernhard replied to FireWire's topic in RockMigrations
Hey @FireWire thx sounds good! I can't remember any reason for having it in __construct I have just added this to the dev branch so we have a month of testing before it's merged to main ๐ -
TemplateEngineFactory Processwire native engine, yield/block tag
bernhard replied to Manaus's topic in Modules/Plugins
I think the latte docs here should answer your question: https://latte.nette.org/en/template-inheritance- 1 reply
-
- 2
-
-
@Sanyaissues and I have been working intensively on a new version of RockMigrations Deployments, which will be moved to RockShell and will support Github Environments. So we thought about having an .env file per environment instead of prefixing every variable: // STAGING.env dbHost=localhost dbName=db_staging dbUser=whatever dbPass=1234 vs STAGING_dbHost=localhost STAGING_dbName=db_staging STAGING_dbUser=whatever STAGING_dbPass=1234 PRODUCTION_dbHost=localhost PRODUCTION_dbName=db_production PRODUCTION_dbUser=whatever PRODUCTION_dbPass=1234 But at the moment I decided to go with PHP files, so there is no need to change anything in your library ๐ Thx for the explanation!