-
Posts
1,475 -
Joined
-
Last visited
-
Days Won
42
Everything posted by gebeer
-
[SOLVED] API for get or find blocks by type or class
gebeer replied to gebeer's topic in RockPageBuilder
Great hint, thank you. My logic for getting the block now looks like /** @var FieldData $blocks */ $blocks = $page->rockpagebuilder_blocks; /** @var Block $contactformBlock */ $contactformBlock = $blocks->get('type=ContactForm, _mxhidden=0'); This gets only non-hidden blocks which is perfect for my use case. -
Indeed, I do. And I'd be very interested to try your module
-
[SOLVED] API for get or find blocks by type or class
gebeer replied to gebeer's topic in RockPageBuilder
Yes, that is correct. I would like to handle this through the block's status, if possible. Since Block inherits from Page that should be possible? Something like $contactformBlock = $blocks->get('type=ContactForm, status!=unpublished'); But: When I hide the block, I get $contactForm->status = 1 When I unhide it, I get same status 1. Looking at the RockPageBuilderBlocks pages in the page tree, they don't seem to have a regular Page status So status in Blocks seems to be handled differently from Page status. Is there already a selector that we could use for finding only unhidden blocks? -
[SOLVED] API for get or find blocks by type or class
gebeer replied to gebeer's topic in RockPageBuilder
Great! Thank you for the quick addition. Very much appreciated. I have a SPA where I have URL hashes to jump to different section on the site like /#kontakt In the info() method for each block I added a property id. For the ContactForm block the id is "kontakt" Now I have link buttons in other blocks that should point to /#kontakt I could have hard coded that. But I wanted to get the block by class name to get the id, just in case it changes one day So in my template that holds the blocks I had this $blocks = $page->rockpagebuilder_blocks; $contactformBlock = $blocks->get('id=1195'); // add the hash to the page object to make it available further down the call stack in all block views $page->contactHash = $contactformBlock ? '#' . $contactformBlock->getInfo('id') : '#kontakt'; ?> <main id="content" sortable> <?= $rockpagebuilder->render(true) ?> </main> With your update I can do $blocks->get('type=ContactForm') which is much safer in my opinion. In case the block gets deleted and recreated, the id would change. -
Hi there, $page->rockpagebuilder_blocks is of type RockPageBuilder\FieldData which extends PageArray and and can be searched like PageArray by id, title etc. Is there a way to get/find blocks by there block type (class name)? Maybe I overlooked something, but couldn't find anything in the docs. I tried things like $page->rockpagebuilder_blocks->get('type|pageClass=Contact'); Would be a neat feature :-)
-
Thank you for this module. Love it. A few weeks ago I accidentally found this gem although it was not listed on your website. IMO this is the slickest color picker in the PW ecosystem, if you only need predefined colors (which is mostly the case in my projects). Very easy to setup and the editors love it, too. This will be new standard in my tool belt for new projects.
-
Nvidia GPU in particular often causes problems. But their drivers have become much better. @kongondo since you chose Ubuntu, you might want to consider giving https://pop.system76.com/ a try. Their releases are quite stable and GPU support is great. Some of the high profile coders on YT use it. I tried it years ago and even back then it was a pleasant experience.
-
Sad to hear about your bad experience with upgrading. Not that I don't want to help. But for these kinds of scenarios I found Claude or ChatGPT very helpful in many cases. Everyone's setup is different. You can go and debug with the help of AI, describing your specific scenario. Since you have a tty (Ctrl+Alt+F3) you can log on and use journalctl and dmesg to find the cause of the problem. The AI will tell you the right commands to find the errors (hopefully). I wish you success!
-
PDFs are created in multiple languages, so that is why :-) I looked at the implementation of the saveToField() method to figure out the exact behaviour.
-
Introduction Automated PDF production is a quite common task for ProcessWire developers. PW does not offer core tools for that. There are some external modules that we can utilize. In this showcase I would like to share my considerations for the tool of choice and my experiences working with it. The task at hand PDF production for product pages on an existing ProcessWire installation acniti.com is a Japan based company that specializes in nanotechnology solutions for gases in water. They have developed technologies to create nanobubbles in water, which can change the properties of water and improve dissolved gas levels through super saturation. Their founder and CEO has chosen ProcessWire for his website and has developed the site himself. He tasked me to add functionality for creation of downloadable PDFs from available product data. The client was forwarded to me from Bernhard because of time constraints on his side. I really appreciate that. The core requirements in detail: Produce separate PDF files for multiple languages, including LTR languages Design a template: cover page, images page, content pages, specs pages Cache PDFs Minimal impact on existing template code Tool choice: RockPdf module I had done some PDF generation before, using mPDF library directly. While there are other options available for ProcessWire (Pages2PDF, MakePDF) I decided to use Bernhard's RockPdf module since it seems the most feature rich and well maintained one. It is also based on the quite popular mPDF library. Reasons for choosing RockPdf Auto-reloading of PDFs in concert with RockFrontend This can save a lot of time (and strain on the F5 key), since the PDF is automatically reloaded every time a code change is made. All it requires to achieve that is one line of code: $pdf->save(__DIR__ . "/tmp/{$filename}.pdf", preview: true); Easy font loading I needed to load different fonts depending on the content language. Here is, how I did that RockPdf CSS utility classes They really came in handy, since they are specifically targeted at mPDF, using pt units. I could easily set some base colors with $pdf->setLessVars([ 'col-muted' => '#dddddd', 'col-primary' => '#0b54a2', ]); The utility classes that I mostly used are those for widths, margins and paddings. They are quite handy for setting up the layout of the PDF. Easy file saving to PW field For caching the created PDFs I utilized a RockPdf convenience method to save the files to a field on the product page $pagefile = $pdf->saveToField( page: $page, field: self::PDF_FIELD, filename: $filename, append: true, ); Implementation Modular approach for minimal impact on existing code I created two modules: Main module ProductPdf: non-autoload, holds all logic/templates for generating PDFs and required markup (download button) Module ProductPdfHooks: autoload, hooks: Page(template=product)::render displays PDF for download Pages::saved(template=product|product-type) creates PDFs in all languages and saves them to field for caching Re-usage of existing template logic There was quite a lot of logic (some of it rather complex) already available in the project that I wanted to utilize. Mainly for specs table creation. I had to do some minimal refactoring of original code. Then I was able to include that logic into my module without having to duplicate it. Benefits of this approach: Minimal impact on existing code Easier to maintain Challenges Limitations of mPDF library mPDF is not good at modern CSS features. It is quite limited in terms of CSS support. I had to do some workarounds to make it work with the layout I needed. Different approach to styling Although RockPdf's utility classes helped a lot, I still had to do some inline styling. Display of complex tables Display of tables in particular was a pain point since mPDF does a lot of automatic adjustments to column widths and distribution that I needed to disable in order to get the desired results: // Ensures that tables retain their original proportions $mpdf->keep_table_proportions = true; // And adding autosize="1" attribute to tables. Page headers, footers, margins and page breaks The RockPdf module docs have some great examples for setting up headers and footers, margins and page breaks. I used those to set up the layout of the PDF without having to read too much into the mPDF docs. Minimal impact on exisiting code base This was overcome by the modular approach I described earlier and it worked out really nice. The only addition to the original product template file for rendering the download button, was calling a static method from my module: <?= ProductPdf::renderDownloadbutton($page) ?> That button requests the page URL with a query parameter. The display of the PDF for download is handled through a Page::render hook PHP DOM manipulation of existing markup necessary Since I reused existing logic for constructing specs tables, I needed to add some inline styles and change some URLs on the fly. I used native PHP DOMDocument for that. There is a feature in the RockFrontend module that offers DOM manipulation tools with a nice API. I would have loved to use those but at the the time of working on this project, I just was not aware of their existence. The result Product pages on acniti.com now have a download button that allows the user to download the PDF of the product page in their language. See it live here The PDF is loaded from the cache field on the page, which is updated every time a product is edited and saved. If no cache file exists, the PDF is created on-the-fly and cached for future use. It is presented to the user in a new browser tab for viewing and downloading. The PDFs feature a clean layout / design which corresponds to the acniti branding. Cover page: Content pages: Specs table: Feedback from the client The client has a lot of experience with ProcessWire which one can see from looking at their website at acniti.com. He gave me great feedback on the project: Erik van Berkum, acniti LLC Lessons Learned and conclusion PDF creation in PHP is still not an easy task. The most popular library, mPDF, has some restrictions related to CSS that can make it tedious to work with. Especially when it comes to more complex layouts and tables. Using the RockPdf module for the task was a great choice. Its API is very well designed, offers a lot of conveniences and is well documented. Bernhard responded quickly to my questions and provided great support. In conclusion, the ProcessWire ecosystem offers great tooling for PDF creation that makes life for developers more enjoyable :-) Future considerations Would I use this approach again? Well, that depends mainly on the requirements. For this task the chosen tooling worked very well and I am happy with my choice. But for more complex layouts/designs that require modern CSS features, I would prefer rendering PDFs through Chromium browser using puppeteer or a self-hosted gotenberg service.
- 5 replies
-
- 12
-
Not so sure about that. But the GH repo on the extension page links to tailwndlabs. EDIT: Actually I would not trust that extension since they are clearly not linking to their own GH repo and therefore pretending to come from tailwindlabs.
- 242 replies
-
- 1
-
- visual studio code
- vsc
-
(and 2 more)
Tagged with:
-
Then I'm not a real programmer :-) For me the extension is still working on Cursor Version: 0.40.1 VSCode Version: 1.91.1 Commit: 58b91712431381a1b75817cd3437cee35dddcb30 Date: 2024-08-24T02:52:24.035Z Electron: 29.4.0 ElectronBuildId: undefined Chromium: 122.0.6261.156 Node.js: 20.9.0 V8: 12.2.281.27-electron.0 OS: Linux x64 6.10.6-zen1-1-zen But that is on Linux. I tried it on a project that I setup with your TW/UIKit profile, so there is LESS involved. Since this extension comes from tailwindlabs, the creators of Tailwind, I would go with it.
- 242 replies
-
- 1
-
- visual studio code
- vsc
-
(and 2 more)
Tagged with:
-
Yes, using VSCodium. Running smoothly for the last 3 years or so. Prefer that over the original binary from Microsoft that comes with all their telemetry shenanigans. Only drawback is that some extensions from the official MS extensions store are not (yet) published at https://open-vsx.org/ which is the default extensions source in VSCodium. But you can still manually download the vsx file from the official store and install. Other than that, I'm very happy with my telemetry-free VSCode experience. Depending on your distribution, you can find the package in the official repos of your distro or in the AUR for Arch (which I'm on btw). Cannot confirm that. On my end it feels ok. But then, I don't have the comparison with MS windows. Can only compare it to Cursor which (unfortunately) is only available as AppImage. And those are not as well integrated into your OS as native packages are.
-
Actually that post is where I got to learn that we can use loaded() for initialisation. It is great that you discussed this in such detail there, especially the drawbacks. I thought, since the OP seems to have a lot of expertise in using page classes, I'd ask him if he had found a more elegant way than using loaded(). And putting everything together that I have learned about page classes, I will avoid initialisation of props using loaded() with DefaultPage going on from here ?
-
This was most likely what caused that behaviour. So yeah, we have to be very mindful there. Guess I needed to learn that the hard way ? Great suggestions here. And thanks for your thoughts on properties in page classes and the very well structured example you give. There might be a good reason to avoid them, since they add overhead to Page loading. And I haven't found other methods like init or ready that are suitable for attaching those properties. Since my main goal with them was to have sensibly named props that intellisense can discover, I might as well stick with methods. After having worked for some time with implementing those props via the loaded() method in the past, nowadays I avoid them for the reasons mentioned.
-
What a great writeup! Thank you for putting your time into this. It could be part of the official docs for page classes. I would be interested in your reasoning behind naming the base class BasePage. In the docs that you linked, Ryan suggests DefaultPage. In https://github.com/processwire/processwire/blob/3cc76cc886a49313b4bfb9a1a904bd88d11b7cb7/wire/config.php#L202 it says: So are you using BasePage to not have every Page be based automatically on DefaultPage? I'm asking this, because I am using the DefaultPage class quite a lot and in some cases it caused some minor unexpected behaviours because the class is used for every Page. Would love to see how you implement properties in your base page class. Specifically how and where do you assign them? I utilize the loaded() method to assign those properties and found that it added lots of overhead because loaded() is called for every page that is loaded in a PW lifecycle when using it on the DefaultPage class. And that seemed a bit expensive to me, especially if construction of some properties requires more complex and expensive logic. Here's an example on how I implement custom properties on runtime class DefaultPage extends Page { // trait that provides methods for responsive images use ResponsiveImages; // define statuses that should be excluded from loadingcustom properties const excludeStatuses = array( Page::statusTrash, Page::statusUnpublished, Page::statusDraft, ); public $introTitle; public $introText; // set custom properties in loaded public function loaded() { // load custom properties only for allowed statuses if (!empty(array_intersect(array_keys($this->status(true)), self::excludeStatuses))) return; $this->set('introTitle', $this->text); $this->set('introText', nl2br($this->textarea)); } } I found that loaded() is called for every page regardless of their status. So I prevent loaded() from assigning properties for some statuses where the properties are not needed. Why I want to have custom properties in the first place? Almost all fields in my PW installations get very generic names like text, textarea, rte, image, text2 etc. I am using those fields in different templates for different purposes. Custom properties are a nice way to access those generic fields in a more meaningful manner and have them available in intellisense.
-
Thanks for clarifying this. I hadn't tested it before suggesting it as potential solution. But I had some cases in the past where this resolved my module permission issues.
-
Looking for an AI assistant for code? Consider Supermaven
gebeer replied to gornycreative's topic in Dev Talk
I get better results with Claude Sonnet than with GPT4o. Been using 3.5 Sonnet since it came out. Currently using it through Anthropic API with https://www.continue.dev/. In continue config I disabled the autocomplete and use Supermaven for that. The continue chat interface integrates nice and I could choose from more models if I desired to do so. With the Supermaven pro plan, you get 5$ of monthly credits for the chat models. Don't know about pricing for additional chat requests above that. Couldn't find it on Supermavens website. When going through Anthropic API, the usage cost is very detailed and transparent. Anyways, exciting to see so much going on in the AI assistant space and great to have so many options. -
[SOLVED] Disable "add" button in Page Reference field
gebeer replied to Pavel Radvan's topic in General Support
To simply hide the Add button of the PageListSelectMultiple field for non-superusers or for all, you can use some custom JS. Example: JS in site/templates/scripts/admin-custom.js // custom admin script console.log("admin-custom.js loaded"); /** * Hides the "Add" button for the Inputfield Page List Select Multiple field with given fieldName. * This function is intended to restrict access to adding existing items for non-superuser roles. */ function hideAddButtonForInputfieldPagelistSelectMultiple(fieldName) { // Uncomment the following line to also hide for superusers // config object is globally available in the admin and contains user objecyt with roles array // if (config.user.roles.includes('superuser')) return; const wrapper = document.getElementById('wrap_Inputfield_' + fieldName); if (!wrapper) return; const start = wrapper.querySelector('a.PageListSelectActionToggleStart'); if (start) { start.style.display = 'none'; } } window.onload = function () { hideAddButtonForInputfieldPagelistSelectMultiple('my_pagelist_select_multiple'); // pass in your field name here }; To load that script in site/templates/admin.php ... // load site/templates/admin-custom.js $config->scripts->add($config->urls->templates . 'scripts/admin-custom.js'); /** @var Config $config */ require($config->paths->core . "admin.php"); You could do this with custom CSS. But there you don't have the user role available. EDIT: or you do it with CSS and load it depending on user role in admin.php ? site/templates/admin.php // load site/templates/admin-custom.css for non superusers if(!$user->isSuperuser()) $config->styles->add($config->urls->templates . 'styles/admin-custom.css'); ... site/templates/styles/admin-custom.css /* Hides the "Add" button for the Inputfield Page List Select Multiple field with name "my_pagelist_select_multiple" */ #wrap_Inputfield_my_pagelist_select_multiple a.PageListSelectActionToggleStart { display: none; } Hope this helps. -
Try following the steps here: https://www.perplexity.ai/search/first-steps-in-processwire-cms-vJ0lNwzDRY6eaXW.skftsw This should get you started.
-
IMO your solution seems a bit hacky. Don't know enough about the structure of your module and how you load it in your Page Classes. But here are 2 ways you could approach it. You could use https://processwire.com/api/ref/modules/get-module/ which has an option noPermissionCheck which lets you load the module regardless of the current users permissions. It seems that you have already isolated the required methods into _motif_include.php. You could refactor and put them in their own module (which depends on the main module and gets installed by it). That module could then have different permission settings. That would be a much cleaner approach IMO.
-
Looking for an AI assistant for code? Consider Supermaven
gebeer replied to gornycreative's topic in Dev Talk
Same for me. Installed it yesterday and went straight for the trial of pro plan because of the insane context window of 1 million tokens. If the model sees that much of your codebase it can make much better suggestions. Even on the free plan you get 300.000 tokens context which is way more than any of the other autocomplete models can digest. The speed is absolutely amazing. Guess I will switch from Codeium. -
I am running the current ("old") version of MM. The documentation at https://mediamanager.kongondo.com/documentation/ used to be for that version. Checking the docs, I just realized that they are work in progress. Where has the documentation for the current ("old") version gone? It would be useful (not only for new users of your module) if they still could access the documentation for the current version. This would certainly help to avoid frustration ? When MM Next is out, there should still be documentation for the old version alongside the new one.
-
Looking for an AI assistant for code? Consider Supermaven
gebeer replied to gornycreative's topic in Dev Talk
Thank you for the insight. Just today I had the clear proof that Codeium autocomplete was well aware of the context. At least the last edited file (like they say in their docs). Can't say for sure that it also has context from the whole codebase. Need to check that. Will also give Supermaven a try and see how it compares.