-
Posts
6,674 -
Joined
-
Last visited
-
Days Won
367
Everything posted by bernhard
-
Hey @adrian please apologise for the subject ? I love Tracy - This is either a feature request or a question to understand why you built it in the way it is and maybe just a question of where I find info in the docs ? I'm creating a custom panel for RockFrontend so that RockFrontend can even live-reload pages that throw fatal errors or where latte {include ...} tags point to a wrong file. Until now I needed to hit refresh manually for those files which is tedious in the long run. All I really need to do is to inject my javascript: <?php namespace ProcessWire; $rf = rockfrontend(); $secret = $rf->addLiveReloadSecret(); $script = $rf->addLiveReloadScript(); if (!$script) return; ?> <script> LiveReloadUrl = '<?= $pages->get(1)->url ?>'; LiveReloadSecret = '<?= $secret ?>'; document.addEventListener("DOMContentLoaded", function() { var script = document.createElement("script"); script.src = "<?= $script ?>"; document.head.appendChild(script); }); </script> I even wouldn't need a panel for this, just a hook that adds my markup whenever the debug bar is rendered on the page. But I did not find one. Is there one? But the question remains why adding simple panels is so complicated. I found out thx to @teppo's WireFrame panel that I need to place my panel in /site/modules/RockFrontend/TracyPanels/MyPanel.php, which is already great. But still... if I only want to show an icon in the bar, a label on the panel and some HTML inside the panel body, this is what I have to do? <?php namespace ProcessWire; use BasePanel; class RockFrontendPanel extends BasePanel { // settings private $name = 'rockfrontend'; private $label = 'RockFrontend Panel'; // the svg icon shown in the bar and in the panel header private $icon = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M5 5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2z"/><path d="M19 6h1a2 2 0 0 1 2 2a5 5 0 0 1-5 5h-5v2m-2 1a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1z"/></g></svg>'; private $liveReloadStatus = 'disabled'; /** * define the tab for the panel in the debug bar */ public function getTab() { if (\TracyDebugger::isAdditionalBar()) return; \Tracy\Debugger::timer($this->name); $livereload = $this->wire->files->render(__DIR__ . "/livereload.php"); if ($livereload) $this->liveReloadStatus = 'enabled'; return "$livereload<span title='{$this->label}'>{$this->icon} " . (\TracyDebugger::getDataValue('showPanelLabels') ? $this->label : '') . "</span>"; } /** * the panel's HTML code */ public function getPanel() { $out = "<h1>{$this->icon} {$this->label}</h1>"; // example of a maximize button $out .= '<span class="tracy-icons"><span class="resizeIcons"><a href="#" title="Maximize / Restore" onclick="tracyResizePanel(\'' . $this->className . '\')">+</a></span></span>'; // panel body $out .= '<div class="tracy-inner">'; $out .= "LiveReload status: " . $this->liveReloadStatus; $out .= \TracyDebugger::generatePanelFooter($this->name, \Tracy\Debugger::timer($this->name), strlen($out), 'yourSettingsFieldsetId'); $out .= '</div>'; return parent::loadResources() . $out; } } Maybe it's already possible to do that in a simpler way? I'm thinking of something like this: <?php $wire->addHookAfter("TracyDebugger::getPanels", function($event) { $event->return->add([ 'icon' => '<svg ...></svg>', 'label' => 'RockFrontend', 'body' => $files->render(__DIR__.'/RockFrontendPanel.php'), 'allowMaximize' => 'true', ]); }); Where Tracy would on its own add all the necessary boilerplate that is necessary for measuring timings and memory consumption etc. Thank you for your time and for TracyDebugger in general!! PS: I have the same todo for RockFrontend's topbar ??
-
New concept module: System Config Versions
bernhard replied to poljpocket's topic in Modules/Plugins
In that case it makes sense, I agree. Now we finally identified the important differentiation ? I could also think of a GUI for RockShell that makes it possible to run commands directly from within the PW backend. I could even think of it pushing live feedback via SSE to a console in the backend. And there could also be helpers to run commands from Github actions. That would be a great way to contribute and give something back to something that you get for free. And you should really have a look at the "db:restore" and "db:pull" commands. They are gold when working with migrations and not only then. For example I've so often had the situation where I quickly wanted to play around with something. For example install a new module or show something to a friend/colleague on a meeting. With RockShell you can do all that without worrying. Add fields, install modules, etc etc. When you are done you simply do "rockshell db:pull" and you have the exact same state as before. Oh, and what about the files that the module installed? Just revert changes of your git project and that's it. -
New concept module: System Config Versions
bernhard replied to poljpocket's topic in Modules/Plugins
Thx. I'd just created a RockShell script in both cases and ran that on the server. So it looks like you are rebuilding RockShell, not RockMigrations ? But yeah, if you want a GUI, go for it ? -
New concept module: System Config Versions
bernhard replied to poljpocket's topic in Modules/Plugins
Thx for trying to explain the details. I understand you well. This is why I built RockMigrations. On the last sentence I'm losing you. I'd still be interested in a detailed example of what you are doing and where this is necessary and why. You are explaining extensively the same workflows that I'm using and then you suddenly state that something is missing without going into detail. For example: How to you execute these snippets? Who does it? Did you think of any different approaches? Why do you think this has not been an issue for me so far? -
New concept module: System Config Versions
bernhard replied to poljpocket's topic in Modules/Plugins
Sounds cool! I understand the need for what you are working on and I'd even be happy to add such features to RockMigrations, if you want and think that could make sense, but more on that later ? That was confusing for me because it's wrong ? Not trying to be know-it-all but it might be interesting for you to know that I tried to put all the method arguments in the order of how the method is named, so add-FIELD-to-TEMPLATE means field first, then template. Using PHP8 with named arguments that not that important any more, though. So back to topic. What you are trying to do have actually been my very first approaches when working with migrations. That's how they work everywhere after all. RockMigrations1 even had downgrade() and upgrade() methods and some version_compare to see if migrations should run or not. The reason why I completely removed that was because it has been so much more productive to use just config-based migrations in my daily work. Rather than writing all the logic around the actual migration to make sure migrations are only executed once I made sure that no matter how often they run they produce the same result. That works great in 99% of my use cases, though there are situations where it does not and I admit it's not terribly beautiful ? So for example if you have fields added via RockMigrations that you don't need any more you'd do this: <?php public function migrate($rm) { // cleanup $rm->deleteField("old_fieldname"); // actual migration } So while this would just work it's really not beautiful and it would log "Field old_fieldname not found" on every run. You can prevent that by adding TRUE as second parameter, but it's still not really beautiful. I've also thought of adding something like this: <?php public function migrate($rm) { // cleanup $rm->once(function($rm) { $rm->deleteField('old_fieldname'); } } The problem here is: How does RM know, what that "once" means? What if it fails? What if inside the once was multiple steps like in your example, for example looping over 100 pages and after 50 it breaks? What do you in your module in such a case? What I've done in such cases was to create an import script based on RockShell that I could run whenever needed. Manually. In the best case only once ? I could also run this script locally as often as I want, try everything out, see the result, and if it's not working I'd just do "rockshell db:restore" and try it again. Once everything works I push to production and run the script there. This is what your script would look like in RockShell: <?php namespace RockShell; use function ProcessWire\rockmigrations; class DemoMigration extends Command { public function handle() { $rm = rockmigrations(); $rm->createField('richtext', [ 'type' => 'FieldtypeTinyMCE', // ... ]); $rm->addFieldToTemplate('richtext', 'basic-page', 'textarea'); $pagesToUpdate = $this->wire()->pages->find('template=basic-page'); foreach ($pagesToUpdate as $pg) { $this->write("Updating page #$pg ..."); $pg->setOutputFormatting(false); $pg->set('richtext', $pg->textarea); $pg->save(); } $rm->removeFieldFromTemplate('textarea', 'basic-page'); $this->success("Hooray - we are done :)"); return self::SUCCESS; } } Put that in /site/assets/RockShell/Commands and you are good to go. I'd also really be interested in how you work with docker. I've seen https://github.com/poljpocket/processwire-docker and your statement sounds very interesting. So I'd love to hear more about that if you find time. -
Hey @adrian could tracy show the execution time of the page even when tracy is toggled off? I think it would be nice to see this to get a feeling of how fast the website is for public users. Tracy and all the panels sometimes make sites a little slower, so I sometimes toggle it off completely, but then I don't see the loading time any more. Would that make sense? What do you think?
-
I've literally yesterday thought of adding this to RockFrontend! I thought I'd add /site/templates/htmx and whatever file you place there would be available as yourdomain.com/htmx/[filename] So for example placing modal.php there you could use hx-get="/htmx/modal" What do you think? Not sure what you mean by JSON api support. Maybe I'm missing something?
-
This feedback is so great, I have to share it with you ? Thx so much @FireWire and thanks for brilliant fluency module!! Just pushed v5.2 to the main branch!
-
Yeah, with more complicated things stackoverflow is often still nicer, because you often see that some solutions are outdated and there are better solutions in 2024. You often don't see that in AI, as it just presents you the solution that it thinks is the best. But for things like "oh yes, 644, why did I even ask" it's a lot more efficient to ask the AI inside cursor and not Google. From my first month of using cursor I'm a little shocked how much I am using it and how much I must have been googling before ?
-
Of course you have to check. It's the same with any source... AI, web, manual, other devs... You always have to think about what they tell you and then you have to decide what you do and take the responsibility for your actions. Cursor gives you the option, it does not fire the command by itself. If it did I wouldn't use it.
-
It really helps with all the little annoying time consuming tasks. Ever forgot what the chmod command is to set permissions to "-rw-r--r--" ? Or even faster: Just hit CMD+K and type this: It will present you with this terminal command: Hit CMD+Enter and done ✅
-
What would happen if anybody uploaded such a zip to a regular user's file field? It would still be a zip, right? Isn't there a setting to automatically unzip on upload? Would that be a problem? I guess no, right? It would just unzip it and then we'd have a .php file in the field, but I think that would need to be allowed in the field settings. And even if allowed I don't think pw would at any time execute this file?
-
Using RockFrontend you could also do this, for example inside a saveReady hook: <?php use Wa72\HtmlPageDom\HtmlPageCrawler; $dom = rockfrontend()->dom($page->body); $dom ->filter("a") ->each(function (HtmlPageCrawler $node) { $href = $node->getAttribute("href"); if (!$href) return; if (strpos($href, "http://") === 0) return; if (strpos($href, "https://") === 0) return; $https = rtrim($this->wire->pages->get(1)->httpUrl(true),"/"); $node->setAttribute("href", $https.$href); }); $page->set('body', $dom->html()); I've added docs for it on the dev branch: https://github.com/baumrock/RockFrontend/tree/dev/docs/dom
-
Hey Ryan, thx for all the updates and congrats on the launch! I am seeing a horizontal scrollbar on screen sizes around 850-900 in chrome, but only when I size the window without devtools!
-
Ajax-driven autocomplete search with native JavaScript
bernhard replied to Stefanowitsch's topic in Tutorials
You can just add a hidden field that is populated on page save from the content of each RepeaterMatrix or RockPageBuilder item. That field can then be queried by any search. In RockPageBuilder you get the benefit that you can simply add a dedicated method to every block: <?php // Text.php public function searchIndex() { return $this->body; } // Accordion.php public function searchIndex() { $index = ""; foreach($this->items as $item) { $index .= $item->headline . ": " . $item->body . "\n"; } return $index; } Thats not a built in feature but as all RockPageBuilder blocks are dedicated PHP files and classes things like this are super easy and clean to implement. This solution would obviously add additional data to the database, so fields are not queried directly. But that adds more options in presenting your search results. See here for some insights: -
Front-end edit of textarea images in child page
bernhard replied to MarkE's topic in General Support
I don't have much time, but do you think this is related? https://github.com/processwire/processwire-issues/issues/1706 -
Imagine you want to display an image on your website. Easy. Imagine you want to open the bigger version in a lightbox. Easy when using UIkit (or any other framework). Imagine you only want to add the lightbox link only if there is actually a larger version than the one displayed. Easy when using LATTE ? <div n:if="$img = $page->image" class="uk-background-muted uk-padding-small uk-text-center" uk-lightbox > <a n:tag-if="$img->width > 800 || $img->height > 400" href="{$img->maxSize(1920,1920)->webp->url}" > <img src="{$img->size(800,400)->webp->url}" alt="{$page->title}" /> </a> </div> This takes care of so many things with so little and clean code: the n:if on the outer div makes sure that we don't get any errors if no image is uploaded the n:tag-if makes sure that the anchor wrapper is only added if the original image is larger than the thumbnail Note that you can assign $img directly in the n:if condition. No need for an additional {var $img = $page->image} at the top. For some that might be too short, but I like it ?
-
RockPdf - Fast and fun TailwindCSS-like PDF creation for ProcessWire
bernhard replied to bernhard's topic in Modules/Plugins
v1.5.0 adds support for directly using fontawesome icon markup like <i class="fa-brands fa-youtube"></i> Just download FontAwesome and start using FontAwesome markup in your PDFs right away! ?? Docs: https://www.baumrock.com/en/processwire/modules/rockpdf/docs/fontawesome/ -
Hey @Jon sorry for the trouble, but I have a little present for you ? v1.5.0 adds support for directly using fontawesome icon markup like <i class="fa-brands fa-youtube"></i> The release is less than 10 minutes young and ready for download at https://www.baumrock.com/en/releases/rockpdf/ Docs are already there as well: https://www.baumrock.com/en/processwire/modules/rockpdf/docs/fontawesome/ Hope that helps you and many others ?
-
I'm working on improving the RockPageBuilder config page and added toggle buttons instead of checkboxes as they are a lot more intuitive (especially if you have default=yes toggles, where you'd need to use a negative meaning with checkboxes, eg "dont use the widgets feature" vs. "use widgets feature yes/no" The default toggle looks like this in ProcessWire: There it is not really obvious which value is selected. Is the grey one the active item or the shiny white one? I think this is much better: So I'm wondering if that should be a global default for toggles in AdminStyleRock - what do you think?
-
Ok... AI did indeed help with this problem again! I told cursor "@[link to this topic] can you help bernhard?" and it gave me instructions what I can do and check. So it told me to place a file in /.well-known/foo.txt to check if my rules work. That was a great "idea" because I was able to check the rules without issuing a cert all the time. I also had a look at PW's .htaccess file and looked how it is done there. Mixing everything together with some trial&error brought me to the working solution: RewriteEngine on # don't proxy requests to the .well-known folder RewriteCond %{REQUEST_URI} ^/\.well-known/.* RewriteRule ^ - [L] # send other requests to uptime kuma RewriteCond %{HTTP:Upgrade} =websocket RewriteRule /(.*) ws://localhost:3001/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule /(.*) http://localhost:3001/$1 [P,L] Maybe it helps someone else ?
-
I'm hosting uptime-kuma on my server as docker container behind a reverse proxy. The domain is set to redirect http://uptime.example.com to https://uptime.example.com Unfortunately my proxy does not let the letsencrypt request through and fails: [23-Jan-2024 10:19:05] INFO | check domain "example.com' [23-Jan-2024 10:19:05] INFO | certificate is valid until 2024-02-03 16:45:10 (11 days left) [23-Jan-2024 10:19:05] INFO | certificate is in renewal period [23-Jan-2024 10:19:05] INFO | renew cert [23-Jan-2024 10:19:05] INFO | Using certificate authority: "https://acme-v02.api.letsencrypt.org/" (PRODUCTION). [23-Jan-2024 10:19:05] INFO | Getting endpoint URLs. [23-Jan-2024 10:19:05] INFO | Account "foo" already registered. Continue. [23-Jan-2024 10:19:05] INFO | Requesting Key ID. [23-Jan-2024 10:19:05] INFO | Sending signed request to "https://acme-v02.api.letsencrypt.org/acme/new-acct". [23-Jan-2024 10:19:06] INFO | Start certificate generation. [23-Jan-2024 10:19:06] INFO | Token stored at: /home/panel/www/.well-known/acme-challenge/local-check-123456 [23-Jan-2024 10:19:06] INFO | URL: http://example.com/.well-known/acme-challenge/local-check-123456 | HTTP code: 200 | HTTP body (first 100 chars): <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" conte [23-Jan-2024 10:19:06] ERROR | a Let's Encrypt error occurred: Local resolving checks failed for domain "example.com". Please ensure that your domain is locally resolvable! This is the proxy setup for HTTPS: # dont proxy letsencrypt requests ProxyPass /.well-known ! # uptime kuma directives ProxyPass / http://localhost:3001/ RewriteEngine on RewriteCond %{HTTP:Upgrade} =websocket RewriteRule /(.*) ws://localhost:3001/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule /(.*) http://localhost:3001/$1 [P,L] Any ideas? AI is not smart enough to help me ?
-
I don't think that this is true any more with HTTP2 because browsers can download multiple files at once. That's why I didn't implement something like this in RockFrontend. But I'm not sure ?