Leaderboard
Popular Content
Showing content with the highest reputation on 10/05/2024 in all areas
-
My pleasure. The hooks and inputfields APIs in ProcessWire make module development an absolute dream. It's very satisfying seeing the results you can get with just a little code, and working on modules provides a motivation to dig into the core code so I learn a lot in the process. Also, another shout out to @adrian's amazing Tracy Debugger, which has been transformative for my coding. I have several more modules in progress so watch this space. 🙂19 points
-
In the last couple of weeks I’ve been to several cities in Spain, France and Italy. I’d never been to any of those countries before (or Europe for that matter), so it was an exciting trip. Though the goal was for my kids to broaden their horizons and experience other parts of the world, as well as spend time with my parents and family. We got back this week and have been recovering from jet lag (another thing I’d not experienced before). The 6 hour difference was no problem getting there, but coming back, it’s a little harder to adjust! Next week I turn 50 years old (ugh), and then the following week I’m back in Europe again, except this time in the Netherlands on a bike trip with a client, and without my kids and parents. I’m not sure I’ll be able to do many core updates during the 10 day trip but do expect to have internet access this time, so will at least be online regularly and hope to be here in the forums. After that trip, I won’t be traveling again for a long time, and the focus will be on getting our next main/master version out. I noticed this week that @Robin S is now beating me as our most prolific module developer, with 72 modules! Great job and thanks for all the great modules Robin S.!3 points
-
@ryan Happy upcoming birthday! Great to see that you're getting some travel in! M'dude, you're going to be in the Netherlands. The core can wait IMHO 👍 We had a great time when we last visited, enjoy! @Robin S doing the lords work 🙏3 points
-
Welcome to the 50s. And nice to know that @Robin S is so prolific. I sometimes get asked by clients what happens if I have a collision with a proverbial bus, and it's a really good selling point to be able to say there's another Kiwi (what we New Zealanders call ourselves after our national bird - not a fruit) who's a major contributor to the system I use for most of my sites, and who's also developed some quite high profile national sites with it. I suppose I should get a move on and write some modules, but I guess it's testimony to the ProcessWire ecosystem that I haven't needed to as yet (well I have, but they're for specific client business processes, so not something I can release publicly.) Speaking of broadening horizons and geography, even though Robin is on the same island as me, we're at opposite ends, and he's at least a 12 hour drive away. I know I shocked my mother-in-law when she came here, because her idea of an island was that there'd be sea all around, and I took her on a trip that involved very full days with 3 or 4 days without ever seeing the coast.3 points
-
Not sure what you mean here. The ProcessWire admin for non-superusers is the most minimal CMS back-end I've seen. It's just the page tree, which is easily understood even by beginners because it corresponds to the front-end structure. And from the page tree editors can only edit the pages that have the templates you allow for them. If the page tree is still too overwhelming you customise it with hooks. If the editors only need to deal with pages in a particular branch of the tree you can conditionally set the top-level parent of the tree. $wire->addHookBefore('ProcessPageList::execute', function(HookEvent $event) { /** @var ProcessPageList $ppl */ $ppl = $event->object; if($event->wire()->page->process !== 'ProcessPageList') return; if($event->wire()->config->ajax) return; // If the user has the "editor" role... if($event->wire()->user->hasRole('editor')) { // Limit Page List to only show a particular page and its descendants $ppl->id = 1085; } }); Or for more complex situations you can control which pages are "listable" individually:2 points
-
In ProcessWire as always you have the freedom to choose from a variety of options. What fits best for you and the editors depends on a lot of factors, but I'd say your options are (from easy, but with default style and options to hard with full freedom): Stick with PW page tree, organise your pages well and properly define access of your users so they see only what they need to see. That's probably the easiest solution, but you get the default PW backend which has pros and cons. Use a module to adjust the backend to your needs. Possible modules are AdminRestrictBranch or the dashboard module. Or any other module that helps you to customise the backend. Create custom admin pages. This is still very easy and you stay in the "safe" PW backend, which means you don't have to take care of everything on your own and you can build on top of proven and battle-tested concepts and code. Create a custom backend. The ProcessWire backend is on its own just built around the PW api, so you can build something similar on your own. This is by far the option with most freedom but also the hardest option, as you have to take care of almost anything (access control, rendering, navigation, etc...) Somewhere in-between those options might be to enhance your frontend with some shortcuts, like buttons to "add new blogpost" or to "manage items" or whatever. These buttons could directly link to backend pages, so it might be easier for clients to find what they need, because what many often forget is that even though the PW backend is super clean and easy to use, there is a barrier or gap between "frontend" and "backend" that for clients is sometimes harder to grasp than one might think! RockFrontend, for example, comes with ALFRED, which adds hover info with an edit icon: And when clicked it opens the dedicated page from the backend directly in a modal in the frontend: There are other modules with similar solutions as well. You have the freedom to choose 😉1 point
-
Thx, but I don't think it's very smart 😄 It's really just PHP, that's one of the main reasons why I like latte so much. They don't reinvent PHP or invent another syntax, they just use PHP and add some helpers here and there. It's really easy to understand, for example create the file php-demo.latte: {file_put_contents('php-demo.txt', 'latte-is-cool')} This will do two things: It will create the file php-demo.txt in the folder of the .latte file It will output "13", which is the strlen of "latte-is-cool" You can think of { ... } being similar to <?= ... ?>. That's why tracy will instantly work inside latte via {bd(...)} as bd() is a globally available function without any namespace and it does not return/output anything. To prevent our previous example from outputting anything you can simply add "do": {do file_put_contents('php-demo.txt', 'latte-is-cool')} Also when using {var $foo = 'whatever'} there is no output. Now consider the following file "php-demo.latte": {wire()->page->id} This will cause the following error: You can click on "PHP" and then you see which file it actually renders and why wire() is not found: Once you click on that file link, it will open up in your IDE: So you can see that this is the compiled PHP file that latte uses to render your .latte file. And your IDE shows, that wire() is not available! So using our IDE we can fix the issue: Which will add this to the PHP file: Now since we can't add the use statement to our latte file like this: {use function ProcessWire\wire} {wire()->page->id} We just use the inline syntax for namespaced functions: {ProcessWire\wire()->page->id} And that's all you need to understand 🙂 But I'll probably add the functions api without namespaces to all .latte files once you help me get the mentioned issue sorted 😉1 point
-
I have been able to get it to work. I think it was sneaky syntax that wasn't really clear and easy for me to miss! I think the challenge in our conversation was that it was hard for me to describe more robustly because it ended up being difficult to understand myself. I'll recap. Let's assume I have a file called 'buttons.latte' and inside of it I have a couple of reusable definitions, this goes along with what I was describing earlier- nothing new. {define button-default, $label} <button class="bg-blue-200 hover:bg-blue-300 transition-colors"> {$label|noescape} </button> {/define} {define button-danger, $label} <button class="bg-red-500 hover:bg-red-600 transition-colors"> {$label|noescape} </button> {/define} I can import all of those definitions using {import} and the file name in my templates and other components/partials/files etc. Then I use {include} with a specific name rather than individually from different files. Using {define} and {import} is a little like named exports in JavaScript. {import 'buttons.latte'} <h1>Welcome to the internet cafe, have a Latte</h1> {* ...A bunch of stuff on the page... *} <div class="modal"> <h2>Do you really want to do that?</h2> {* Here I can use include with button-danger instead of including a file like 'button_danger.latte' *} {include button-danger, label: 'OK'} {include button-default, label: 'Cancel'} </div> {* ...The rest of the stuff on the page... *} The problems all started when using an {embed} on the page and then attempting to use an element that was imported, in this case button-danger and button-default. Here's what happens if we create a reusable "modal" that can be embedded and populated using blocks. {import 'buttons.latte'} <h1>Welcome to the internet cafe, have a Latte</h1> {* ...A bunch of stuff on the page... *} {embed 'modal.latte'} {block content} {* The block inside the embed tag is a whole new scope and fails when you attempt to include the buttons imported outside that embed tag *} <h2>Do you really want to do that?</h2> {include button-danger, label: 'OK'} {include button-default, label: 'Cancel'} {/block} {/embed} {* ...The rest of the stuff on the page... *} This was confusing because this {embed} and the content inside the {block} were mixed in with the rest of my template markup so it wasn't easy to catch where the issue was actually occurring. Even with the improved error reporting you added, the error that was shown by Latte by this did not make it clear it was a scope issue so at the time I thought it was because the 'buttons.latte' file was not being included whatsoever. The real story here is that Latte does not provide errors with specific messages for scoping issues. It will just show an error that looks like a file can't be found. HOWEVER. Solution ahoy! The following does work because the {import} is placed inside the {embed} tag so now the elements in 'buttons.latte' are now scoped within that embed tag so they can be included by name. In this example I've left the {import} at the top of the page because that would necessary should you want to use one of the button elements anywhere outside the scope of that {embed} tag. {import 'buttons.latte'} <h1>Welcome to the internet cafe, have a Latte</h1> {* This required that import above for the parent scope *} {include button-default, label: 'Here is a random button'} {* ...A bunch of stuff on the page... *} {embed 'modal.latte'} {import 'buttons.latte'} {block content} {* These work now that the buttons have been imported in the embed scope and can be included by name *} <h2>Do you really want to do that?</h2> {include button-danger, label: 'OK'} {include button-default, label: 'Cancel'} {/block} {/embed} {* ...The rest of the stuff on the page... *} I thought that Latte's scoping rules were specific to files not tags- in this case the {embed} tag. This was really easy for me to miss in a couple of larger Latte files where noticing the {embed}{/embed} scope while surrounded by a bunch of other markup was really not easy to catch, and combine that with the not-so-clear errors, it made it seem like {import} was broken, but it isn't. This is no problem once you add that to your knowledge of Latte. That said, I think that the fact that Latte uses {include} interchangeably for files like "button.latte" and elements created using {define button}{/define} can be a point of confusion. Well, it was for me. So, Latte scope is serious business but errors leave something to be desired 🤣 I've been able to use all of the features of Latte with RFE and it's great. Hopefully my mistake will help others. Thanks as always for your help @bernhard this was a tough one 🤷♂️ This is a pretty smart solution! I didn't think about that. I'm still wrapping my head around the Latte === PHP situation. My brain hasn't connected the two yet. Slowly but surely... Since I already had it set up, I'm using the hook RFE provides to access the Latte instance and a custom extension. I posted this above, but I'll mention it here again in case it's helpful for someone to see this info all in one place. (I added a custom filter as well) <?php use Latte\Extension; final class CustomLatteExtension extends Extension { /** * Define functions available in all Latte files * These can be anything ProcessWire related or not, any functions defined here will be available everywhere */ public function getFunctions(): array { return [ 'wire' => fn (?string $property = null) => wire($property), ]; } public function getFilters(): array { return [ 'bit' => fn (mixed $value) => new Html(filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 1 : 0), 'bitInverse' => fn (mixed $value) => new Html(filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 0 : 1), ]; } } $wire->addHookAfter( "RockFrontend::loadLatte", fn (HookEvent $e) => $e->return->addExtension(new CustomLatteExtension), ); That hook in practice does this: // Defining the wire() function above means that you can use it in any Latte file anywhere {wire()->modules->get('SomeModule')} // The $property parameter is a little syntactic sugar that for calling wire() with arguments for a native feel {wire('modules')->get('SomeModule')} // I have a need to output booleans to the rendered markup and it was getting laborious. My use case is outputting to AlpineJS that exists in // Latte templates {var $someVariableOrPageProperty = true} function someJsInYourTemplate() { // This does nothing because you cant echo a boolean to the page and neither will Latte if ({$someVariableOrPageProperty}) { //... } // You can do this, but it gets ugly when you're working with a lot of booleans. if ({$someVariableOrPageProperty ? 1 : 0}) { //... } // With the custom filter. Clean. if ({$someVariableOrPageProperty|bit}) { //... } } Anyway, those are what I came up with and it's working out great for me.1 point
-
1 point
-
Happy birthday! Now that you remind me, next week I will also turn one year older, (ugh) :P1 point
-
Glad you're having a good time on our continent, Ryan! Nice choice! It is so cool you are about to celebrate your first half of a century! Seems like you're having a great life so far. But you didn't have all those 50 years to work on PW pitifully, as you had to spend time on childhood and etcetera. Your next half centuries you won't have this excuse))) So waiting for the things to come! Congratulations to @Robin S! Your modules really make the ecosystem a place to thrive! Thank you!1 point
-
1 point
-
Thanks. That's an unknown. It's not worth the time of making a PR if Ryan doesn't want it as a PR. When it's a module I and everyone else can use it right away. When it's a PR then it's something that maybe becomes usable someday, maybe not...1 point