Leaderboard
Popular Content
Showing content with the highest reputation on 11/14/2021 in all areas
-
Most of us have been through this. You start not knowing anything and do most of things by trial and error. But at the end you come out stronger and more knowledgeable. I do not think there is a shortcut here. To learn something is to find yourself in the place of ignorance first. You should solve your tasks one by one, searching the forum, reading the code, asking the community if you get stuck. I do not think anyone is "too beginner". But everyone has to start from where he is at. There are a lot of docs, great forum posts and other resources spread around. but there is probably not a single tutorial to complete and become a ProcessWire master. Take you time, be patient, have fun along the way)) P.S. And just to address you specific questions a bit, I would recommend you this link... and many more here - just search for "create form")))6 points
-
While the first link provided by @Ivan Gretsky seems like a good starting point for creating forms with InputfieldForm, note that this is not exactly the simplest thing to do. Personally I've never found it very intuitive, or very useful for that matter. (This, of course, is a highly opinionated statement — so take it with a grain of salt.) In my opinion it's almost always easier to just create a basic HTML form: <form action="." method="POST"> <label for="your_name">Your name</label> <input type="text" name="your_name" id="your_name"> <input type="submit"> </form> ... and then process form input in the template file: <?php namespace ProcessWire; if ($input->post->your_name) { // always sanitize user input (https://processwire.com/api/ref/sanitizer/) $your_name = $sanitizer->entities($your_name); // do something with input echo "<p>Hi " . $your_name . "! What's up?</p>"; } Of course that's a super-simplified example, but hope you get the point. Using InputfieldForm, InputfieldText, InputfieldSubmit etc. is doable, but in many cases they mainly just add a whole new layer of complexity without much actual benefit ? If you want to save values to custom database tables, you need to work with "raw" SQL through the $database object. But here's the kicker: The preferred way to save data in ProcessWire is through pages. A "page" is essentially a general purpose data storage object: you can use pages to store publicly viewable (front-end) content, as well as (back-end only) categories or tags, or form entries — or really whatever it is that you need to store. The API is largely built around working with pages: creating, finding, reading, modifying, and deleting them. Again: you can work directly with database tables through the $database object, but that's not really what ProcessWire was built for. (It's useful for special situations, but for a beginner that's not what I would recommend starting with.) As such, likely the easiest way to save data would be to create a template with fields related to your entries, and then on each form entry creating, populating, and saving a page using that template. That's not necessarily scalable if you expect tens or hundreds of thousands of entries in a short period of time, but apart from that it will work just fine ? ... although, to be fair, a lot of folks just use Form Builder for building and managing forms. It's a commercial module, but in the long term it saves so much time and makes things so easy that it's well worth it. Especially if you're working on projects that you get paid for. This depends on the context: if you're writing code in a template file (/site/templates/some-template.php) then you can use $modules. If you're writing code within a class that extends the Wire object — module, Page class, etc. — then you need to use $this->modules instead. And if you're writing code within a module, you generally use wire('modules'), etc. Here's a docs page explaining when to use what: https://processwire.com/docs/start/api-access/.4 points
-
2 points
-
Thanks @ryan. All the pieces were already in place. I've been working on this concept for a while now and with live preview, everything has come together. @Ivan Gretsky That's just with custom CSS I'm loading in the admin. Like this: // assuming RM field is called 'builder' and matrix-type is called 'builder_column' #wrap_Inputfield_builder [data-typename="builder_column"].InputfieldRepeaterItem > label.InputfieldHeader { outline: 1px solid #777; outline-color: #777; background-color: #777; } @bernhard The 'options' field (powered by Mystique) has a plethora of options for each matrix-type. The 'Image' matrix-type has a 'width' and 'height' field that if set, will set it to the specified width (->width(width)), specified height (->height(height)) or size (->size(width, height)). If no values are entered, it uses the original dimensions.2 points
-
Another good option for partial HTML updates is morphdom which transforms the existing dom nodes to match the new incoming HTML without discarding any elements. That way, any dom events, scroll positions or css transition states will be kept on the existing elements. No specific markup required, nor changes to how you set up dom events. It's used in Phoenix LiveView which is pretty close to our use case (live updates from server-rendered templates).2 points
-
Now that we're in the last couple months of this year, I've been trying to implement a lot of plans that we discussed early in the year. That meant a lot of updates to repeaters in the last couple of weeks (and likely more on the way). But this week, I've been working on another thing we discussed earlier, which is bringing automatic save and live preview capabilities to ProcessWire, independently of ProDrafts, and with full repeater support. In ProcessWire, it's your own code (in template files) that renders the site’s output. ProcessWire delivers a $page to your template file(s), and your template file(s) render it in whatever manner they see fit. In this environment, we need an automatic save capability before we can have a live preview capability. Working on that automatic save capability is what I've been doing this week. I figure that once that is finished, then we'll be able to start developing the live preview capability. I'm glad to report that we now have automatic save fully functional and have tested pretty thoroughly with every Fieldtype/Inputfield I can think of. Unlike in ProDrafts, it also fully supports repeater, repeater matrix, and even nested ones. Actually, it looks like we'll be able to support everything you would want it to. But it's early yet it'll need a lot of testing before its production ready. I've built it into a module called PageAutosave. It requires the latest version of ProcessWire on the dev branch (3.0.189). I'm not yet positive whether this is going to end up as a core module or not. I thought I would gauge interest and see. For now, I'm posting a limited release test version of it in the ProDrafts support board and ProDevTools support board, if anyone is interested in trying it out. The PageAutosave module lets you configure whether you want it enabled for all pages or just for unpublished pages. When needed, you can also optionally choose to limit it to certain templates, certain fields, or certain user roles. Autosave isn't always desirable as it literally means your changes get saved to the $page as you type, so I imagine it's something people might use more for unpublished pages. Autosave is one side of the coin and live preview is the other. The current goal for live preview is to not depend on any particular view or admin interface and instead to just simply have the option of having a window open to a page in the website that detects changes and updates them automatically. So if you had one window open to the page edit screen, and another window viewing the page being edited, you could observe your updates as you made them. Once that is working reliably, we can decide on how best to put an interface around it, and maybe some will also like the option of just having two browser windows open wherever they want them. This won't be the kind of live preview where you'll see every character as you type it, but more likely you'll see updates a second or so after you make them. That's because your edits have to be auto-saved in the page editor, ProcessWire has to call your template file(s) to render the output, and the live preview has to update whatever has changed since the last update. It's that last part of it that I'll need the most help on. Some of you have mentioned htmx as a potential way to accomplish that and I think it looks compelling. And if you know of any other [likely JS-based] tools or technologies that we should also look at, please reply and let us know. I've bumped the core version to 3.0.189 primarily because the PageAutosave module requires updates that are in this version. This version also continues some longer term build out of a couple Fieldtype interface methods across multiple modules, and adds a new 'sorted' JS event that is triggered during sort actions (also used by PageAutosave). But if you aren't going to be installing the PageAutosave module, there's no urgency to update to 3.0.189 if you are on the dev branch and already using 3.0.188. Thanks for reading and have a great weekend!1 point
-
Like last week, this week, updates continued on the core and matrix repeater fields. Repeater and matrix fields can now be configured to use fewer pages. When set, it won't create placeholder pages for repeater items until at least one repeater item exists for a given page and field. This can drastically reduce the number of pages consumed by repeaters in your system, and even more so if you are nesting repeaters. Eventually, this will become the default setting, but for now we are playing it safe and making it optional with a new toggle that you'll find on the Details tab when editing a repeater or matrix field: After enabling the "Use fewer pages..." Setting, the "Find an optionally delete unnecessary pages" checkbox will take care of cleaning up anything that isn't necessary for existing repeaters already in the database. If you have a large site with a lot of repeaters, this could be deleting a lot of now irrelevant stuff, so just be aware of that and backup ahead of time to be safe. Thanks to @Jonathan Lahijani for the idea/suggestion. Also new this week is the ability to copy and paste repeater items, as well as to clone above or below existing items. It handles this by replacing the existing "clone" icon action with a dialog that now lets you choose among various related actions. Among them is the ability to copy/paste from the same page or between different pages. The only requirement is that the repeater (or matrix) items are from the same field. See the video below for an example of how this works: This works with either Repeater or Repeater Matrix fields. But if you want this feature in Repeater Matrix, you'll want to upgrade to ProcessWire 3.0.188 and download the new version posted today (v8 beta) in the ProFields download thread. The ability to copy/paste repeater items was an idea originally from @David Karich and a module he developed called Repeater Matrix Item Duplicator. Thanks for reading and have a great weekend!1 point
-
Hi all, have just upgraded to 3.0.184, a little late to the party. Not sure about a few things and would love to understand better. It seems to me like there is now an increased over-reliance on inline style width declarations on Inputfields. To me, they seem like extra bits of unnecessarily specific css that impede easily customisable CSS. I can't wrap my head around why they are needed. Surely CSS and flexbox can do this better, easier, with much greater flexibility, less javascript, less thread work, less bytes and less layout shifts? In previous versions, data-colwidth on everything with a width worked just fine. Was very easy to target li[data-colwidth] and use calc to add a margin if desired and use flexbox to flex like a champion, taking up available space. Lovely, simple, flexible and lightweight. Now it seems some wrappers have a style="width: ..." where once they had data-colwidth and some Inputfields have data-original-width. Each can technically be over-ridden using ...sigh... a multitude of repetitive !important declarations, but that is not ideal. Is there something I am missing? Is there a reason the inline width styles were needed? Is there a benefit I am unaware of? Can I help provide possible alternate solutions to whatever use case necessitated it? In a similar vein, I also got to see maxColHeightSpacer for the first time. Can't say it was a pleasure for it to appear. Similar to the use of inline style="width:..." on Inputfields, in the age of flexbox I don't understand why a spacer div with inline height is needed. Is there a need for it I just don't get? Hadn't seen it before but found some info from a few years ago here: Anyway, I would honestly love any insight on any of this. Would love to understand the 'why' and, if possible, help.1 point
-
What you're coming across is actually this: https://github.com/processwire/processwire-issues/issues/829 I've been trying to fix it for a while now: https://github.com/processwire/processwire-issues/issues/829 https://github.com/adrianbj/TracyDebugger/issues/35 https://github.com/adrianbj/TracyDebugger/issues/36 https://github.com/adrianbj/TracyDebugger/issues/58 But unfortunately it still doesn't work properly in some scenarios. Could you describe your situation and maybe we can get Ryan involved again to see if he has any ideas or changes he could make to the PW core to prevent this.1 point
-
@ryan - Ok, I tracked that error down. Uninstalling the module is what triggered that. It turns out that during the uninstall, if I dump $change from inside this foreach: foreach($changes as $key => $change) { I get: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'page_autosave_changes' doesn't exist But back to the more important issue of why the module doesn't actually work for me. Firstly, the module does work on my local dev environment, but on a live servers, it doesn't. I still keep getting: "ajax error: error" If I inspect the Network tab, I see a 500 error for: /page/edit/?id=8074&modified=1636913850&fields=body but there don't seem to be any PHP errors, so I went looking in the apache logs and I am seeing these: [Sun Nov 14 09:54:27.525633 2021] [proxy_fcgi:error] [pid 24560:tid 139854520305408] (70007)The timeout specified has expired: [client 154.5.162.190:51384] AH01075: Error dispatching request to : (polling), referer: /?live_preview=1 [Sun Nov 14 09:54:34.477785 2021] [proxy_fcgi:error] [pid 24560:tid 139854579054336] [client 154.5.162.190:51384] malformed header from script 'index.php': Bad header: {"error":false,"message":"Ajax, referer: /page/edit/?id=8074 [Sun Nov 14 09:54:34.477904 2021] [proxy_fcgi:error] [pid 24560:tid 139854579054336] [client 154.5.162.190:51384] AH01070: Error parsing script headers, referer: /page/edit/?id=8074 [Sun Nov 14 09:54:34.477913 2021] [proxy_fcgi:error] [pid 24560:tid 139854579054336] (22)Invalid argument: [client 154.5.162.190:51384] AH01075: Error dispatching request to : , referer: /page/edit/?id=8074 The Network tab shows Response Headers: content-length: 0 I did a a little investigating to see if there are any known issues with SSE and these errors, but nothing useful so far.1 point
-
@bernhard you are a gem in this community. I knew I had ran across that code from a trusted source. Anyways, somehow everything seems to be working fine this morning. Love it and thanks!1 point
-
Hi, welcome to the ProcessWire forums! The questions in your recent threads are kind of broad and it’s difficult to tell what exactly you’re struggling with and what you already know. I’m assuming you have ProcessWire installed and working, and you want to save some user inputs from the frontend into the fields you have created in PW’s admin area. Saving fields using the ProcessWire API First of all, you need a page to hold the data. Perhaps this page already exists, then you just get it using selectors, or maybe it’s the page you’re already on. Likely you want to save a new page for every time a user fills out the form, so let’s create a page: $p = new Page(); $p->template = 'form-submission'; //or whatever your template is called $p->parent = wire('pages')->get('/submissions/'); //or whatever the parent page is called Nice. Now, to get the information from the form you’re going to need ProcessWire’s input capabilities. You can find out all about it at the link and you’ll need to make some decisions about validation/sanitization depending on the data you’re dealing with, but the gist is to get stuff from the POST request sent by your user and put it into fields. Let’s say your user submits their e-mail address and you want to save it to a field called email: /* You see the name “email” three times: * 1. The first is the field you set up in the admin area. * 2. The second is the sanitizer that makes sure the thing the * user sent you actually looks like an email address. * 3. The third is the name of the input from from the form the * user filled out and submitted. */ $p->email = $input->post->email('email'); //Now you will probably want to fill out some more fields the same way: $p->comment = $input->post->textarea('comment'); $p->title = 'Form submission from ' . date('Y-m-d H:i'); Then you save the page and you’re done. You can go to the admin backend and check out the newly created page. $p->save(); However, we haven’t talked about the frontend bits yet. Creating a frontend form and sending submissions to ProcessWire Once again much depends on what you actually want to do, but for simplicity’s sake, let’s say this all happens on the same page. You have a ProcessWire template associated with a template file and all the above code is in it. Now we put the form into the same file. Basically the form is sent to the same page it’s coming from, but as you’ve seen above, you can still create the new page wherever you want. So here’s a simple HTML form that submits to the same page: <form method="POST" action="<?php echo $page->url; ?>"> <label for="email">Your e-mail address</label> <input name="email" type="email" /> <label for="comment">Your comment (no swearing!!!)</label> <textarea name="comment" rows="5"></textarea> <input type="submit" value="Send"/> </form> Note how the names of the input fields match the names we accessed earlier using $input->post(). Putting it together If you simply copy all my code blocks into your template file, you’ll probably notice that it tries to create a new page ever time the page is loaded. You’ll need to figure out if there is a form submission at all before you deal with it, and otherwise just skip that part and show the blank form. You may just check if there is anything in the comment variable. So here is your complete template file: <?php namespace ProcessWire; $thanks = ""; if ($input->post('comment')) { $p = new Page(); $p->template = 'form-submission'; //or whatever your template is called $p->parent = wire('pages')->get('/submissions/'); //or whatever the parent page is called $p->email = $input->post->email('email'); $p->comment = $input->post->textarea('comment'); $p->title = 'Form submission from ' . date('Y-m-d H:i'); $p->save(); $thanks = "Thanks a bunch, we value your feedback!"; } ?> <!doctype html> <html> <head> <title>Post a comment</title> </head> <body> <?php if ($thanks !== "") { echo "<h1>{$thanks}</h1>"; } ?> <form method="POST" action="<?php echo $page->url; ?>"> <label for="email">Your e-mail address</label> <input name="email" type="email" /> <label for="comment">Your comment (no swearing!!!)</label> <textarea name="comment" rows="5"></textarea> <input type="submit" value="Send"/> </form> </body> </html> Now I’m not saying you should put this exact code on your website, but it’s a demonstration of the most bare-bones things you’ll need to get input from users: A HTML form that generates a POST request and some PHP code to receive it. It doesn’t matter where these things are or what they’re called or how they’re generated. In this example we’ve written the form by hand because it’s easy, but you could just as well generate it from ProcessWire’s fields.1 point
-
1 point
-
@fuzendesign Are you using TracyDebugger module? If not, I can highly recommend it. It is a big time saver being able to easily dump and inspect your code and makes developing with PW even more enjoyable.1 point
-
So great to hear. I’ve been following HTMX for a while. I like the dev’s pragmatism. Makes me think of Ryan and PW also.1 point
-
Super excited about the e-commerce projects hopefully becoming ProcessWire shaped!1 point
-
Maybe my day was already way too long and I don't understand your question and needs here... but... what do you want to accomplish with this and where? In the frontend? In the backend? Within a page, a form, or... Your question and code sample somehow don't match for me.1 point
-
I'm not sure what I was talking about back then, but you can simply do something like this: <?php namespace ProcessWire; class MyPage extends Page { public function init() { $this->addHookAfter("ProcessPageEdit::buildForm", $this, "buildForm"); } public function buildForm(HookEvent $event) { $page = $event->process->getPage(); if(!$page instanceof self) return; // $page is a MyPage object now $form = $event->return; // get the title field and add a note if we find it if($f = $form->get('title')) { // note that we use $page->foo() and not $this->foo() $f->notes = "I am a MyPage object and my foo() method says: ".$page->foo(); } } public function foo() { return 'MyPage-Foo!'; } } Note that the hook in this example never gets executed! You'd need to trigger it once. Where you do that is up to you - I'm usually doing that via RockMigrations (https://github.com/BernhardBaumrock/RockMigrations/blob/5cafb3c6c0f2004b8d4087ca243cc0d5f771dd11/RockMigrations.module.php#L377) but you could also add it in init.php <?php $tmp = new MyPage(); $tmp->init(); This adds a little overhead since we load an additional instance of MyPage on every request but I do much more prefer that over getting more and more of a hook hell on larger projects. The way I showed above is much cleaner and keeps everything where it belongs: To the MyPage class. Now once you open a MyPage in the PW backend you get the note on the title field and you know where to look for that hook: In MyPage.php - not in ready.php, or init.php, or another module, or wherever else...1 point
-
No worries @gebeer. You are correct, I should have just hooked into Form Builder and grabbed what I needed there. I’m learning as I go. I’ve never used hooks and this is my first true use of Form Builder. Your efforts were not in vain. I learned much from it that I’m using what I learned for another part of my mini app. The alternate direction I took was passing a Customer ID from form 1…onto form 2…and then querying the email there. It was a much better and smarter solution than I had initially planned since knowing the customer (ID) would allow me to auto populate a field with just **that customer”s** email, instead of sending all customer emails to that dropdown and expecting my client to choose the appropriate email tied to the customer. As you can imagine, not very smart but I was trying to bang something out that would hold me up for a week while I developed this app further. Also, I pointed to that other thread because it contained a similar problem I was encountering. In retrospect, I should have started a **new** thread on that post instead of posting to Solved. Another learned moment. And just yesterday, I understood why my code in that other thread wasn’t working. Thanks again for your initiative. The PW community is great.1 point
-
Welcome to the forums, @Shohan Arafat! If you want it in admin it must be a custom process module. You can learn how to make one here (for example). But I am not sure I really understood your needs. This is your 1st post, and it might be you meant something very different. Please explain your needs better if my link above is too much or completely irrelevant.1 point
-
There are form tutorials you can find in the forum that you can use for what you need.1 point
-
I quite agree. I think that, stylistically, the front-end editing approach is a good model to follow (assuming it works) and has the added benefit of already being familiar to many developers.1 point
-
I think there's no way around this issue except for the developers to adapt their code a bit, have their page working without javascript, or a full page reload. I tried to check if there was a way to somehow "reset" the javascript but it seems there's none. What I like about this is how it's somehow reminiscent of front-end editing's method D. Maybe it could be something to rely on for the live preview as well ?1 point
-
I get you now. I've been dealing with this a lot, where custom Inputfields have been updated and InputfieldPageAutocomplete, CKEditor, Selectize, etc stop working. I have been using ProcessWire's Inputfields API to reload them ?, i.e. initFromInputfield, Inputfields.reload, .trigger('reloaded'), etc. htmx has this event htmx:afterSettle that is triggered after the DOM has settled, after a swap. I use that like this: htmx.on("htmx:afterSettle", function (event) { // RUN POST SETTLE OPS // this function will get the details of the trigger and target event // it will then call ProcessWire's Inputfield's methods or an Inputfield's own methods (e.g. InputfieldPageAutocomplete) // to reload the Inputfield that has been updated, e.g. adding a new autocomplete to the DOM via htmx runAfterSettleOperations(event) }) The only challenge I've had with this is Selectize (I reported the error in another post) but I managed to resolve that. If you are not currently using htmx, I imagine you could emit an event after your preview has been updated and reload your elements and their attached events. I'd probably use MutationObserver in that case, and have it watch a top-level wrapper element, e.g. something that wraps all the markup in the preview window. It can detect changes anywhere in the DOM. You might run into performance issues though, if you cast your net that wide, as opposed to watching, say, a number of ul lists.1 point
-
Thanks for your thoughts @ryan. You raise some good points here. Setting the entry level as low as possible is desirable. Do you mean when coding in htmx or vanilla JavaScript/jQuery? Maybe have a look at htmx onLoad method? There is an example here for third-party integration, although I am not sure if it is applicable. Other than that, many of htmx attributes are inherited and can be placed on the parent element. htmx aside, with JavaScript event bubbling/delegation, even with vanilla JS, if you placed the events on the parent element, future events should be picked up. Another option is MutationObserver. It is a beast though! Or maybe I misunderstood the question?1 point
-
@kongondo Thanks, great ideas! Your SSE examples are eye opening. I think I understand a lot of this but not yet all of it, since I'm not deep in htmx yet, so I'll have to return to this once I do. But part of what you covered was the external interface to it that the web developer implements, and I think that's a good place to start, so I'll focus just on that. I understand the benefits of a separate config file, but am not sure how many would go to the effort of maintaining it for live preview. Maybe if PW auto-generates it somehow with functional defaults, it might help. Pros/cons aside, when we need something outside of the defaults, personally I'd rather declare this with markup classes or attributes in a manner similar to how we do for Markup Regions. Maybe I'm lazy, but it's already easy and familiar, and not much new to remember. Like with Markup Regions, ProcessWire can remove the attributes when they aren't needed, so no need for any output downsides. But if I had to declare it separately from the markup, I'd at least like to be able to do it in Setup > Fields > field rather than having to edit a .json file (and maybe this is a good fallback either way). I agree that it's good if the default behavior is to swap the entire <body> / refresh the window. But when we want to declare what gets updated with a live preview, here's an example of one way we could do it. If I wanted a particular section of markup to update automatically when the field "images" changed, I'd like if I could just do something simple like this, where div#gallery is an existing bit of markup a already there, like this: <div id='gallery'> // markup for an image gallery </div> …and I just add a class to that existing markup tag... <div id='gallery' class='pw-field-images'> // markup for an image gallery </div> ...or maybe a custom attribute instead: <div id='gallery' pw-field='images'> <!-- markup for images gallery --> </div> I'm not suggesting to add additional markup, but just an additional class or attribute to existing markup. If that same images field was also used elsewhere in the page, then I could just use that class (or attribute) again, and it would update too: <div id='sidebar-photos' class='pw-field-images'> <!-- markup for the sidebar photos --> </div> One thing I noticed in coding ProDrafts live preview is that field-level granularity often makes little difference in the end result. Replacing larger blocks of markup often is just as effective and visually identical. So I'd want to be able to say "if any of these fields change, update this…": <div id='content' pw-field='title,body,images,sidebar'> ... </div> For fields that already contain markup (like CKEditor), or fields that get rendered from their own /site/templates/fields/* template file, ProcessWire could automatically add the appropriate class by wrapping the value with it (this is similar to what ProDrafts and PageFrontEdit do): <div class='pw-field-body'> <!-- value of $page->body, div wrapper added automatically when live preview --> </div> This enables it to work automatically, without the developer doing anything at all. The only minor downside is that occasionally that can interfere with a CSS/JS selector, like if you are trying to target h1 + p:first-child, where the <p> is part of the CKEditor output but the <h1> isn't. Live preview still works, but the CSS/JS selector no longer matches when live preview is on. It's simple to work around though, and of course only affects output when someone is live previewing. Whether using a separate config file or not, if we use htmx, I think it should be a silent player and the developer wouldn't even need to know anything about how to use it. I'd like to avoid an external config file where I'm having to add 'htmx-attributes' arrays. I don't think we need the level of granularity or features in live preview that would demand the user know this kind of stuff. (Though fine for advanced users if/when needed). The more automatic it is, the more likely one is to use it. I think we'd also like the flexibility of not being dependent upon any particular library in case we ever decide to replace it with something custom. Side note, but one issue I also noticed in coding ProDrafts live preview is that when you update an element that has JS events on it that were added in document.ready, then they no longer work. For instance, an images gallery might have events that make thumbnails open in a lightbox or something. When the images gallery live updates, the lightbox no longer works unless the events were added to the whole document. Does htmx have some magic for this kind of situation?1 point
-
I have had a play and some of the implementation is literally, trivial ?. SSE SSE is so simple it is unbelievable at first. You only need two things: in our case PHP and a modern browser...batteries included! Example adapted from w3schools. More examples at MDN. server-side <?php namespace ProcessWire; header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $time = date('r'); echo "data: The server time is: {$time}\n\n"; flush(); client if(typeof(EventSource) !== "undefined") { var source = new EventSource("/some-processwire/url/"); source.onmessage = function(event) { document.getElementById("result").innerHTML += event.data + "<br>"; }; } else { document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..."; } I tested it and it works fine but was getting errors about wrong mime type if I set the headers later, but that is to be expected. Live Preview Refresh - Partial and Full - ajax - htmx I have this working nicely (will try post the demo later today). The premise is: A change happens. The editor client sends the data to the server to save the change (autosave - was just mimicking this part). Server saves and sends back a response including the name of the field that has been saved and the page ID back to the editor client. The editor client communicates with the preview client (a separate window). It sets the page ID and the name of the changed field name in its message to the preview window. The preview window client (htmx) receives the page id and the name of the field that has changed and sends a request to the server to fetch the rendered contents of that field for that page. The server sends back the html for htmx to swap in the DOM. In this simple communication, we have the field name match the html id of the element we are swapping. E.g. <h2 id='headline'>My Headline</h2> This is simple and straightforward and easily achieves partial refresh. However, it will be difficult to handle the more complex cases I mentioned in my previous post. It will also be somewhat dictating how the developer should name their html attributes. In addition, it still leaves the developer's templates with htmx attributes that will not be needed in production. This got me thinking. Live Preview Wrapper - idea #1 In this case, the live preview window would add a wrapper html element around the rendered template file markup. The wrapper html element would have the htmx attributes instead of the template file markup. This idea didn't go very far. It would inevitably lead to broken / incorrect markup in the preview window as the outermost markup of the template file would be <html> tags. In addition, while it solves the problem of unrequired htmx attributes in production, it doesn't solve the problem of selective rendering. It still wouldn't' know what parts of the markup in the preview window to swap. Live Preview Config - idea #2 What if instead of live preview / ProcessWire suggesting things like naming of elements, standard markup, etc. that are needed in a template file in order to get selective markup rendering, we implemented it the other way round? What if the developer instead, via config file(s), passed properties to live preview to implement the htmx attributes needed for a template file / page? The default would be full page refreshes. However, if live preview detected that a partial refresh config was available, it would implement those. This would offer the granularity needed for partial refreshes. If live preview did not receive / detect any configs, the default would kick in. Live preview would remain as agnostic as possible. It could be something like this (assumes use of htmx): A developer places their live preview partial refresh configs in /site/templates/live-previews/basic-page-live-preview.php (or basic-page-live-preview.json). ProcessWire detects that the template file basic-page has a partial refresh config in .../live-previews/basic-page-live-preview.php When ProcessWire loads the preview window, it injects the htmx attributes for each named element in the live preview configs in the corresponding elements in the preview window DOM. Injection is done by a ProcessWire live preview near-agnostic JavaScript script that only runs in the preview window. When the autosave happens in the editor window and the preview window gets informed, htmx kicks in, responding to an event and fires htmx.trigger(elem, event). The element in the markup with the corresponding event listener (hx-trigger='this-element-event-fired') will pick up the event fired by htmx.trigger() and query the server for the changed markup. The attributes set in #3 will ensure only the markup that has a change will fire. Server sends the response back and the element's hx-swap (or even hx-oob) picks it up and swaps the DOM. #6 is still not straightforward though. What will live preview / ProcessWire send back? Here too the config at #1 can help. E.g. it can tell ProcessWire to send <p>My Field Value</p> or just send the value of the field back, i.e. 'My Field Value'. The former is still complicated. Instead of telling ProcessWire I want <p>My Field Value</p>, how about tell it, via the configs at #1 that if requested field is field A, send me back the raw value (e.g. a plain text) but if the request is for field B, use this defined function in basic-page-live-preview.php to send me the rendered partial markup for field B? All ProcessWire would do is pass the value of field B to the said function. This value could be a Pageimages object. The function would iterate the images and send back the 'gallery' markup back to htmx to swap. example live preview config <?php namespace ProcessWire; $livePreviewConfigs = [ [ 'field' => 'headline', 'element_id' => 'my_headline_element_id', 'htmx-attributes' => ['hx-swap' => 'outerHTML', 'hx-trigger' => 'headlineevent'] ], [ 'field' => 'images', 'element_id' => 'gallery', 'handler' => 'renderGallery','htmx-attributes' => ['hx-swap' => 'innerHTML', 'hx-trigger' => 'galleryevent'] ], ]; function renderGallery(Pageimages $images) { // we only need the <li> since we are using innerHTML swap // the target element is <ul id='gallery'></ul> // -------------- $out = ""; foreach ($images as $image) { $thumb = $image->size(200, 200); $out .= "<li><img src='{$thumb->url}' alt='{$image->url}'></li>"; } // --------- // sent back to htmx to swap return $out; } When the preview window loads, ProcessWire sends $livePreviewConfigs to the browser, e.g. ProcessWire.config.LivePreview. The preview window JavaScript would consume it like this: client set htmx attributes @note: JavaScript written in the browser, not tested, could have errors. // get live preview configs const elementsConfigs = ProcessWire.config.LivePreview // loop through configs, find the corresponding element and set their htmx attributes for (const elementConfig of elementsConfigs) { const element = document.getElementById(elementConfig.element_id) if (element) { const htmxAttributes = elementConfig['htmx-attributes'] for (const [prop, value] of Object.entries(htmxAttributes)) { element.setAttribute(prop,value); } } } htmx-ready DOM changes via live preview injection (runtime only in live preview window): <h2 id="my_headline_element_id" hx-swap='outerHTML' hx-trigger='headlineevent' hx-vals='{"field":"headline"}'>My Headline</h2> <!-- gallery --> <ul id="gallery" hx-swap='innerHTML' hx-trigger='galleryevent' hx-vals='{"field":"images"}'> <li><img src='image-1-src' alt='image-1-alt'></li> <li><img src='image-2-src' alt='image-2-alt'></li> </ul> I have partly implement the above (just hard-coded configs for now) and it works nicely. Takeaways ProcessWire's live preview remains as agnostic as possible. Developer give instructions to live preview (via configs) and not the other way round. Live preview ships with minimal defaults, including full page refresh. It seems like a lot of work on the developer's part if they want partial refresh. However, this partly matches the complexity of their template files. Besides, some of the functionality (e.g. renderGallery()) could be something they already use in their template, hence reusable. Developer does not need to have additional markup in their template files in order to see live previews when editing. This means no cleaning up afterwards. Their templates remain clean. Live preview only alters the DOM (of the rendered template) at runtime. Developer can name their markup attributes as they wish. Developer can decide what needs live preview and what doesn't, even on the same page (e.g. hx-select). ProcessWire stays out of the developer's way, as usual.1 point
-
@Ivan Gretsky That part I know I can handle. It's the front-end JS side of it that I don't know a lot about, yet. I mean this just to suggest the possibility of establishing some sort of guidelines for how a developer might implement selective rendering. I don't know what those guidelines would be, and just use that GET var as an example of how a request might indicate a change for a particular field (named "body"). Trying to communicate that in the simplest example possible. The developer would decide what that meant in terms of output that should be produced. But perhaps this example is too far from the context, I'm still learning how this SSE stuff works. PW can't generate partial markup for anything (outside of the admin at least), so it would be up to the site developer to implement whatever methodology we suggest for this, at least if they want higher performance live updates. From what I can tell, htmx can handle both cases... replacing partial markup, or selecting a part (hx-select) from a larger portion of markup, like the full document. @kongondo Yes you are right, it wouldn't be very practical. But the benefits of selective markup rendering may be enough that it's worthwhile for us to establish some guidelines for supporting selective rendering. Like that ?change=body example earlier, that's what I was trying to communicate there even if it's likely not a good example. The guidelines could be as simple as just letting the site developer know when they can skip over expensive things to render, like primary navigation or footer, etc. Something like if($config->livePreview) { // skip rendering unnecessary primary navigation } else { // render primary navigation } I was actually thinking of "body" as a PW field name, and the developer would decide what that means to the output in terms of what should be replaced. But you guys have a lot more experience with htmx than me so I think my example is likely flying a little blind, as my focus so far as only been on the auto-save part that identifies field changes. I need to start playing with htmx. ? The way that ProDrafts live preview works is by having the editor window communicate to the preview window and notify it when a field changes (JS based window-to-window communication). The preview window does pull a fresh/updated copy of the rendered page, but it attempts to only replace the markup that changed. It does this by having formatted values for fields like "body" include an extra <div> around them that identifies what it is, and then ProDrafts can replace just that element with JS. You can see things as you type in the editor, with a short delay, unless your page is very slow to render. This works well for formatted text fields (which is usually most of what one wants to see in live preview), but it does have to refresh the full document for other things where the site developer may be responsible for more runtime markup related to the field output. @monollonom ProDrafts only reloads the whole frame as a last resort. For most cases it can update just what changed. This is helpful for avoiding a very visible and distracting page refresh. Such page refreshes kind of kill the live-preview effect and can more easily draw your attention away from the content you are working on. I don't know if it'll be necessary to introduce a 3rd party dependency or not (htmx), but it does seem like a good place to start, even if we end up rolling our own later. That's more the domain of ProDrafts, and PagesSnapshots (another module in development). I think any kind of core autosave and live preview will focus on just those features (autosave and live updated view) and making them as good as they can be. It might be that one only wants to use these features for unpublished pages, but I think that's where it is most useful in the first place. If you wanted autosave/live preview on a published page, you could always clone it and then replace the original (or not) when you were done. This is essentially how PagesSnapshots manages drafts.1 point
-
Unfortunately, we cannot get a way from a full page refresh no matter how desirable a partial refresh is. htmx deals with selectors, to find the element(s) to swap. It would be very onerous for ProcessWire to read through a template's code to find the element to swap. This means this: <div hx-get="/some-url" hx-trigger="some_event" hx-target="#body" > </div> OR this.. <div hx-get="/some-url" hx-trigger="some_event" hx-target=".body" > </div> What @ryanis suggesting is that the target for htmx can be read from the GET variable. Whilst this might work for simpler cases, and it is a nice idea, the fact is there are myriad ways for building templates. What about fields that are split into various fragments on the page, how do we update those? What about the concatenated ones? This also means developers need to code for the live preview and in a certain way. Having said that, htmx has hx-vals. <div hx-get="/example" hx-vals='{"myVal": "My Value"}'>Get Some HTML, Including A Value in the Request</div> OK, that might work. This would tell ProcessWire if this changes, give me the changed values for 'myVal'. All these lead nicely to the next headache. What happens when your site goes live? It means you have to delete all those hx-attributes as you only needed them for live preview. Just playing devil's advocate here ?. If live preview was about rendering an Inputfield's content, that's easy. We would let individual Inputfields' __render()s handle it. I am using this quite a lot with partial updates (i.e., only those bits that have changed). However, if it is about previewing the site, how it would appear when rendered on the frontend, then the many difficulties of adapting to a developer's code in their template files make partial updates quite difficult, if not impossible.1 point
-
Thanks for all of the replies, this is great information. It sounds like SSE is the way to go here. I don't have any experience with that yet so am looking forward to learning more. I'm basically repeating what's been said above, but it sounds like htmx has this built in and may be a simple way to get started with it. Since this is a front-end modification (where PW doesn't usually venture into), the more lightweight we can be with it, the better. So we might eventually consider putting together just the functionality that we need for this part, as I know htmx does quite a bit more. But since I know almost nothing about SSE, htmx sounds like a great place to start. Since the front-end comes exclusively from user's template files, I don't think there's a way that ProcessWire can selectively render one thing or another in the page. It has to render the entire page. But it does sound like the actual JS-update part of it could certainly be more selective. For users that want selective server-side rendering of elements, it's certainly possible that we could provide some kind of suggestion that the developer should follow, as a means to increase performance of live preview. For instance, if the URL is /path/to/page/?change=body then the developer might detect $input->get('change') === 'body' and render the markup appropriately. But I think most will just want a drop-in solution that they don't have to code around. And that means how fast it works would have more to do with how efficiently the site is coded and how fast the environment is that it's running is. I don't think this will ever be a "see each character as you type" type of live preview, but I think most of the value of live preview will still be present even with a small delay.1 point
-
Thanks, @ryan! This year's end seems to be even more interesting than its beginning) If that is required for the live preview it should be in the core, IMHO) If I understand it right, the core of what we need to build is something listening to a page save event and refreshing the preview page when it happens. Now we have an autosave to generate the events. The other part is reacting to that autosave. I can see 2 ways of doing that: ajax polling and sse (we probably do not need WebSockets as the preview doesn't need to send anything to the server... yet?). The latter (sse) seems to be a better fit, as it should use less server resources, but might be harder to implement (maybe not). Anyway, htmx supports sse (and even ws to an extent), which makes it a better fit than unpoly that doesn't (at least it didn't not so long ago when I checked). Actually, we could go without htmx, just taking inspiration from the principle it is based on. Though taking the ready-made library could be easier. The other part where htmx (or unpoly, or turbo or...) could help, is refreshing not the whole preview page, but only a part of it. Regenerating the whole page markup could be a long process (those 2 seconds in the OP are way too optimistic for many of the sites I have seen). For example, we could regenerate only one PM block markup and sent it to the preview for htmx to swap. But that would require either some standardization of the frontend or some hookable architecture for a developer to implement. The former would break the core PW principle of leaving frontent to the developer. The latter should be possible and would work nicely when rendering RM based content builders the standard way or with a Wireframe. Unpoly is complete framework, which could be used to upgrade PW admin as a whole, but would probably require to do everything its way. For a one place thing or for a more-work-more-customization htmx is a better suit, as it is lower level, as both of you @Craigand @kongondoagreed. If we bring in unpoly, we need to be ready to slowly redo all the admin area with it (which might be a nice thing in the long term). But for one task htmx is lighter solution. And we could even go without it only getting inspiration from it.1 point
-
One of the many reasons I like htmx. It reminds me of ProcessWire a lot. Out of the box all it does is give you the necessary tools then stays out of your way. Even the author, for some reason, reminds me of Ryan ?.1 point
-
I'd like to add another vote to the notion of using something like htmx or Unpoly - I like them, a lot. Having used them on various projects, both have pros and cons (like most things) and satisfy different use cases. I know PW is quick, and the template caching improves on things, but I recently launched a site that made use of Unpoly's up-preload and up-instant functionality - the perceived speed of page loads makes it feel like a static site. The client's response on seeing the dev preview was "I was not expecting a like for like copy! Especially one that is that fast." (their emphasis; it was a redevelopment of a bad WordPress build). With Unpoly, I find the modal, popup and drawer UI are great. Having those integrated with the dynamic functionality is a plus, and means I don't have to worry about whichever underlying CSS framework is being used or pulling in a separate dependency to provide that. It's not perfect; some interactions, particularly nested ones, need a bit more care to make sure the server responses and front-end attributes are all present and correct. I also make heavy use of the "compiler" functionality. This is essentially your "$(document).ready()" equivalent which lets you initialise your client-side components (like maps or colour pickers, for example) on both initial page load as well as any time they happen to be added to the page dynamically via AJAX as the result of an Unpoly request. htmx sits at a bit of a lower level, and provides events and a plugin system to extend it and hook into different interactions. When you need to do client-side things, you will still need some javascript to get the job done; or you could use htmx's sister project _hyperscript. Either way, I'd be happy to see any of these become part of the PW admin. ?1 point
-
Dear Ryan, I think you have not been "trying" to..., instead, you have been working hard on them (not just big features you mentioned today, but smaller yet still useful ones, as always). Thank you so much! Maybe it is worth taking a look at Unpoly as well: https://unpoly.com/ Various opinions: https://groups.google.com/g/unpoly/c/aKfjcIZKi4w https://groups.google.com/g/unpoly/c/w5mVyqA5zmg Demo site created by the author: https://demo.unpoly.com/1 point
-
Hi Ryan, I just gave the module a quick test with my super advanced out of this world builder setup and wow, this really works well! It even auto-saves Mystique-based fields which is absolutely perfect. Also, replacing a single image-field autosaves correctly too. So sweet.1 point
-
This capability would directly impact how many of my projects I do in Processwire and how many I do in Oxygen/Wordpress/ACF. I am currently only using Processwire for things that will make heavy use of the API for very custom projects, and using Oxygen for all projects that are design/marketing/brochure heavy. Its just so much faster to see changes as I make them. This is what I would like to see, working backward from the user side to the tech side. When I edit a page, as I am changing fields, if I change a text box or a drop down or a range slider to change a heading font size, my page preview is updated as fast as possible, so I can see what that new heading size looks like on the page. As soon as the field is changed, it adds the change to a change history list that I can use to roll back edits I don't like without needing to remember what changed. Next, when I am ready for the user to see my changes I have made to my draft version of the page, I can hit a publish changes button to make my draft version replace the live version of the page. I am assuming editing the page in this live preview mode would need to make a new copy of the page that would replace the live page if you save it. Right now, I must save after every font size change to see if it works, and it doesnt remember how far down the admin page I was, so I must scroll all the way back down to that field and change the size again and hit save again and switch tabs again. Its not a great experience. These changes would be great!1 point
-
Just pushed PW 3.0.188 and the RepeaterMatrix v8 beta to a side-project as it's a nice testing ground for this and it works perfectly fine. No errors or problems so far. The new icon option makes it super clean - if you want. Yet and empty entry with just an icon came up each and every time I updated the RepeaterMatrix field, even when I just opened it.1 point
-
1 point
-
Super cool! Just amazing) This is what we, the Repeater Matrix lovers are waiting))) Are those type icons already present in the new version, or are you saving them for now?1 point
-
This week we have some very useful new additions to both the core Repeater Fieldtype and the ProFields Repeater Matrix Fieldtype. This post covers all the details along with a couple of brief demonstration videos— https://processwire.com/blog/posts/new-repeater-and-repeater-matrix-features/1 point
-
It took a little more time this week to wrap up what I was finishing last week (and the week[s] before). But I think it's now at a good spot to move on to something else, and so I started with some Repeater and RepeaterMatrix updates. In Repeater, a new feature was added that's been requested for awhile: the ability to add items anywhere you choose. Previously you could do it only by adding items to the bottom, and then drag them in place. Now you can click insert before/after [icons] in each repeater item header and it will add the new item in place. It is also depth-aware. This is something that I think will also be useful especially for people using repeaters for page builder type contexts. (Note: the feature does not [yet] work if you have all Ajax features turned OFF in your repeater settings). There's a GIF screencast below that shows you how it works. You can click the "insert before" or "insert after" actions and it inserts a new item in place. Alternatively, if you hover either action for a second, it'll show you where it's going to insert an item and you can click either the the item, or the action, to complete the insertion. There's still likely some optimizations and improvements to make in the JS here but so far it seems to be working well. (I made this as a GIF but for some reason IP.Board won't accept it, so here's a YouTube embed instead): Everything in Repeater also gets inherited by RepeaterMatrix, so you'll find this feature there too. But it's not fully functional there just yet. That's because RepeaterMatrix items also have a "type", so I'm working on a new version of RepeaterMatrix that lets you select a type once you've chosen where to insert the item. Another related feature in progress in RepeaterMatrix at the same time is a dropdown/select option for choosing what matrix type you want to add for items. This is an alternative to the current list of links that appears at the bottom. The dropdown also gives you the option of having groups of related types. More on that soon, potentially next week. Thanks for reading and have a great weekend.1 point
-
@nbcommunication Thanks, I was able to duplicate and fix the issue and it is in 3.0.187.1 point
-
@Ivan Gretsky Hooks have already been added for this, and I think they were present in the dev version I posted in the ProFields board a few months back? I don't remember for sure, but I'm trying to make everything hookable so that people can modify the output as needed, and will keep looking for additional opportunities for that as I build in more related features. @teppo Yes there's a renderAddMatrixItemLink() hookable method for this, and it renders a link for one matrix item type, and gets called for each of them. So you could change the output to be anything you want. This is already present, but with the changes being made, chances are another hookable method will be added related to this. @Pete If I understand correctly, this is already there. See the "Matrix types to allow for adding new items" on the "Input" tab when editing your matrix field. This can be configured in template context which enables you to specify some types for one template and other types for another, etc. @mlfct Thanks, I was able to reproduce that here too and have pushed a fix on the dev branch.1 point
-
We have a hook for that by now: https://processwire.com/api/ref/pages/published/ <?php $wire->addHookAfter("Pages::published(template=yourpagetemplate)", function(HookEvent $event) { $page = $event->arguments(0); $mail = new WireMail(); $mail->subject("Page {$page->title} has been published..."); ... $mail->send(); }); Not sure if that is really what you want though... Maybe you want to send an email when the page is created? Then Pages::added is for you: https://processwire.com/api/ref/pages/added/1 point
-
Just an FYI - the reason the template_id is set to the name of the template in the export is so it is translatable to another PW site where that template will likely have a different ID. This way the import process can use the supplied name to find and then set the local ID for that template.1 point
-
Try setting template_id as an integer: $field->template_id = $templates->get("mm_state")->id1 point
-
@SamC it's really as simple as that: https://processwire.com/blog/posts/new-ajax-driven-inputs-conditional-hooks-template-family-settings-and-more/#new-conditional-hooks Update 2022: $wire->addHookAfter('Pages::saved', function(HookEvent $event) { $page = $event->arguments('page'); bd('page saved'); bd($event, 'event'); bd($event->object, 'event->object'); bd($event->arguments(), 'event->arguments'); }); in the beginning it can be a little confusing when to use event->object, event->arguments and event->return but with the help of tracy you can quickly bring light into the dark: add the code above to the tracy console, set the radio on the right to load it on "ready" (same as placing the code in the site/ready.php file) and save any page: $event->arguments('page') is the same as using $event->arguments(0) that you will see very often and in the tracy dump you see that 0 is simply the key for the first argument in that hookevent. you can also collapse the "data" property of the hookevent and you would see the same: You can also use your IDE to quickly find what the HookEvent is returning/containing in which hook. Let's take the common saveReady hook as an example: We know that the hook is attached as Pages::saveReady (eg because we have read that somewhere) That means that the hook is part of the "Pages" class, which is located at "/wire/core/Pages.php" - that's easy to find out using CTRL+P in VSCode: Searching for "___saveReady" lets us find the corresponding method: Now we see, that we have ONE "arguments", being Page $page - this means $event->arguments(0) would return the page being ready for saving $event->object would be the class, here "Pages" $event->return is the return value of that method, here $data (line 1739) Edit: There's some additional explanations in this post: https://processwire.com/talk/topic/27248-pagestrashtemplatefoo-vs-pagestemplatefootrash/?do=findComment&comment=224659 #search afraid of hooks1 point
-
yes, I already tried it. but I do not know, how to integrate this form in the processwire complex. Do I have to create a page for the form? to submit the form, what do I have to enter in the action attribut of the form tag? just putting the php file? then I skip index.php and all system variables are not available......0 points