Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 05/25/2023 in all areas

  1. Why? This is the way everybody else does it 😉 I'd recommend using a folder in /site/templates, eg /site/templates/FrontendForms - if you choose one in /site/assets the files will not be translatable.
    2 points
  2. Off topic: htmx is in the first cohort of the GitHub Accelerator: https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next/
    2 points
  3. @bernhard My current strategy for detecting development/staging/production environment is currently based on using gethostname() or server_name, depending on the site and setting a custom $config->env variable. Eventually I will switch to a cleaner way when Ryan hopefully officially implements something more formal. I saw your PR and have read the various discussions on this topic. That aside, I would still want guestForceDevelopmentLocal to apply specifically to myself (based on IP or some other method) because sometimes I let people view my local dev environment from the outside and I don't want them to see the debug bar.
    1 point
  4. The separation here is on purpose. Everything in the module folder comes from the module folder. Everything outside comes from the user 😉 Same as with /wire folder and /site folder.
    1 point
  5. Ok, thanks @bernhard!! I have not seen that before and my thought was to keep it all inside the FrontendForms folder and not separated all over the site😉. So I will add an extra folder beside the templates folder for the custom email templates.
    1 point
  6. Hi @MrSnoozles, Thanks for the feedback. Yes, a breadcrumb navigation is planned. I haven't started work on it yet. It will be very similar to Google Drive (i.e. like the demo.filerun). True. And the 'green' is just for testing. So, no decision made there yet. The browser for the recording was zoomed in at around 175% so that could be the reason but I've note this, thanks. Thanks! Hi @fruid. No. As mentioned in the post with the video demo, the next MM will not be available until after the summer at the earliest. I have also just noticed this. It must be related to a newer version of ProcessWire. I am only noticing it in 'All media' view. Pagination is working fine if viewing a single media type. It shouldn't show twice and yes, I have also seen this if in 'All media' view. That footer is not inserted by Media Manager. It is inserted by ProcessPageLister which is the class that ProcessMediaManager extends. The next version of MM will not be extending ProcessPageLister so this error won't occur. Good idea. I'll add this. Sorry about that. I'll have a look. Will do. Totally! Thanks for all the feedback!
    1 point
  7. I'm both embarrassed to say that I didn't even realize this was here (despite it being front-and-center in the UI) and elated to discover it today thanks to this sentence. ♥ PW has so many (amazing) things included, it's hard to keep track. Even though you've built it all, I'm surprised you're able to keep track of them all too!!
    1 point
  8. @Stefanowitsch @szabesz @bernhard @cpx3 All right, the issue should be fixed, haven't tested it, please check it. EDIT: fixed version 1.1.7
    1 point
  9. @bernhard Usually ProfilerPro would be great for this, but in some cases I can't use it because ProfilerPro is a module and I'm timing something that happens prior to modules loading. When PW is in debug mode, it times all of the boot processes, so I can just look at those. I also add other Debug::timer() calls as needed to time specific things. When testing in the admin, you can finish a timer with Debug::saveTimer($timer); and it'll show the result in the Debug > Timers section of the admin. But you can't look at any one instance of a timer. You've got to take several samples before you really know if there's a trend in the numbers. I'm usually looking for consistently 10 ms or more when it comes to improvements. @Ivan Gretsky The cache supports an expiration flag represented by the WireCache::expireReserved constant. It means that rows with that flag are reserved and should never be deleted. If you use the $cache API to clear the cache, it'll do it in a safe way. But if you just omit the caches table from a DB dump or manually delete all the rows in the caches table, then it would be problematic. I agree it would be better not to have to consider this. I'm not sure we need to keep that flag other than for modules, so this is one reason why I'm looking to have the modules use some other storage method for its cache. Though if new caching options came along (Redis, etc.) it would sure be nice for modules to be able to utilize that caching method too, so there are tradeoffs. Ideally, the modules would still use WireCache, but be able to recover easily if its caches get deleted, so that's probably what I'm going to work towards, but it's not as easy as it sounds. @teppo Sounds great! I have no experience with Redis but this server seems to have it and I've also been curious about it. I really like the idea of dedicated cache systems independent of the DB or file system. I'd definitely like for PW to be able to support them, and am glad to hear you are interested in it too.
    1 point
  10. Version 2.1.38: This version now supports HTML5 validation for the 4 new validators added in 2.1.37 by using Javascript. It works by adding min and/or max attribute on the fly to the appropriate input field, if a value has been changed inside the reference field. Please read the full changelog here for more detailed information.
    1 point
  11. // init.php $wire->addHook('/webhook', function ($event) { // http_response_code(403); // or header('HTTP/1.1 403 Forbidden', true, 403); // no exit, apply server logic $event->return = true; }); // ready.php $wire->addHook('/webhook', function ($event) { $this->log->save('webhook', $event->return); // exit(); }); C:\Users\flydev>curl -I -L --insecure https://xx-xx-xx-xx.ngrok.app/webhook HTTP/1.1 403 Forbidden Alt-Svc: h3=":443"; ma=2592000 Cache-Control: no-store, no-cache, must-revalidate Cache-Status: Souin; fwd=uri-miss; key=https-/webhook--; detail=UNCACHEABLE-STATUS-CODE Content-Length: 0 Content-Type: text/html; charset=utf-8 Date: Thu, 27 Apr 2023 14:59:36 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT Ngrok-Trace-Id: b2213e4460d4a73821ac2041f312d8d9 Pragma: no-cache Server: Caddy Set-Cookie: wires=s106arh77i4f9ot1jeh7bvged6; path=/; secure; HttpOnly; SameSite=Lax Status: 403 Forbidden X-Powered-By: ProcessWire CMS Reverse output: Connections ttl opn rt1 rt5 p50 p90 15 0 0.01 0.02 90.35 151.65 HTTP Requests ------------- HEAD /webhook 403 Forbidden GET /webhook 403 Forbidden Tracy dump: ProcessWire\HookEvent data: array 'object' => ProcessWire\ProcessPageView 'method' => 'pathHooks' 'arguments' => array 0 => '/webhook' 1 => true 'path' => '/webhook' 'return' => true <=== $event->return 'replace' => false 'options' => array 'id' => 'ProcessPageView:100.0:pathHooks' 'cancelHooks' => false 'when' => 'after'
    1 point
  12. Steve, I just tested pusher webhook with a URL Hook, I can get the 403 error code, and the $event->return as true from a chained hook. The 403 code is thrown with header('HTTP/1.1 403 Forbidden', true, 403); I am forcing params as it could be interpreted differently on web-server basis. Tested on pw 3.0.199-dev.
    1 point
  13. I'm not yet sure what ProcessWire could do here since it's the template file that controls all the logic of what gets output. But I may not yet fully understand the request, so I'll use an example or what I do understand below. Markup Regions don't have control over what your template file spends time rendering, just what gets output at the end. So there wouldn't be much benefit to having output of partials when it still has to spend the time to render everything, whether used in the output or not. Instead, you would need some logic in your template file in order to selectively render partials, and gain a performance benefit from it: <?php namespace ProcessWire; // render just $part if requested, otherwise render all parts $part = $input->get('part'); // i.e. header, content, footer ?> if($part == 'header' || !$part): ?> <div id='header'> ...header markup... </div> <?php endif; ?> if($part == 'content' || !$part): ?> <div id='content'> ...content markup... </div> <?php endif; ?> if($part == 'footer' || !$part): ?> <div id='footer'> ...footer markup... </div> <?php endif; ?> <?php if($part) return $this->halt(); ?> In the above example, if the page is requested without a "?part=" query string in the URL, then it renders everything (header, content and footer). But if rendered with a "?part=content" query string in the request URL (for example), then it would render and output just the <div id='content'>...</div>.
    1 point
  14. Just an FYI for everyone who may upgrade to the latest PW dev version today. If you make use of AOS's excellent "Add button to check/uncheck all checkboxes" feature, it will break the top level menu dropdowns in the PW admin (and maybe other JS). The fix is to replace: ("[data-no-checkall-checkboxes="1"]") with: ([data-no-checkall-checkboxes="1"]) in: /site/modules/AdminOnSteroids/scripts/aos.min.js
    1 point
  15. Others have mentioned how to do it in the Markup or in $config. If you wanted to do it using JS, this is how: document.body.addEventListener("htmx:configRequest", (event) => { // add XMLHttpRequest to header to work with $config->ajax event.detail.headers["X-Requested-With"] = "XMLHttpRequest" }) https://htmx.org/events/#htmx:configRequest Not meaning to hijack this thread and I haven't read through everything and I don't know how Markup Regions work...just want to mention that I have set up a GitHub repo for htmx + Alpine.js + Tailwind CSS + ProcessWire demos. It is early days but I am hoping to add all sorts of demos there, from basic to advanced ones, including backend and frontend examples. I'll create a thread for this work at a later time. Meanwhile (and I have nothing against the OP wish), I am happy to take on a challenge like 'how would you build this complex page using htmx' . OK, maybe not as complex as this app: From React to htmx on a real-world SaaS product 😁: (too much todo, etc.). Edit First two demos are discussed in this thread:
    1 point
  16. @Pete, This is now ready. I have done two demos. In the first one, Alpine.js populates the modal with values that have been passed to its $store. These include details about a product's variants, client-side calculation of total price, etc. htmx adds to cart and displays success message. In the second demo, htmx populates the modal with values it fetches when the modal opens (via 'buy now'). It then passes the values (via the returned markup + x-init) to Alpine.js $store. After this, it is the same as the first demo...and htmx updates the cart and displays success notice. I was hoping to do a video demo + tut but run out of time. I'll do a better explanation here (or in a different thread) or in a video later. For now, the most important things to note are: I was hoping to do some very basic examples but got carried away with these demos. I hope to do very basic examples with very minimal markup in future. daisyUI is used here for its Tailwind CSS components (e.g. the buttons, modals, etc.). Stating the obvious here, but it is not needed in a final project. Note the folder structure in /templates/. Demos are in their respective folders in /templates/demos/. Although I would have wanted a simpler folder structure so as not to confuse, it wasn't easy to pull that off given that this repo will host various demos. I didn't want to create multiple templates that display the same thing. Hence, the demos .php files do the rendering and the handling of the htmx request for their own demos. This means the template 'products.php' remains clean and just loads the current demo's .php file (a render file, so to speak). The folder /templates/data/ has JSON of the fields and templates used in the demo, in case you want to test in a separate site. _func.php has a number of utility functions including one which can fetch dummy products from the Fake Store API, and use these to create products and categories. Selecting a demo automatically reloads the page. This is achieved via a redirect passed to htmx. Viewing the frontend will greet you with this, very colourful shop! I went wild with all the available daisyUI themes. The current theme can be changed using the select at the top left. This is persisted using a plugin by Alpine.js called persist. It plays very nicely with $store and x-data. Whereas it is very easy to use things like x-data with local data within a component itself (e.g., <div x-data="{ open: false }">), given the size of the demos project as a whole (now and in future), I decided to handle and store most of the Alpine.js stuff in a main.js file. This separation of concerns helps in the long run. All libraries are pulled from their respective CDNs. There are lots of comments in the demo files. Whilst this makes the files longer, hopefully you also get to understand what's going on. Most things are necessarily named verbosely. Things should be very clear. The demo site itself is not finished. E.g. currently, we only have a very basic template if viewing a single product. There is very little attempt to hide my ineptitude with respect to CSS 😁. Any questions, just holla.
    1 point
  17. I'm developing a site using HTMX to swap images in a gallery (it uses 'picture' element and 'srcset' so I didn't want to load all the markup at once) alongside PW regions and it works great. I haven't really encountered any problems or gymnastics -- but maybe my use-case is simple enough? <?php namespace ProcessWire; $imgMarkup = ''; $thumbMarkup = ''; $imgNum = 0; /** Build the gallery fragment (the bit that changes via HTMX) before output */ if ($page->gallery && $page->gallery->count() > 0) { $imgNum = $input->get('img', 'int', 0); // get image index number from GET var (AJAX and no-JS) $galleryImg = $page->gallery->eq($imgNum); // get the image from index // The image markup (actually rendered from a custom page class) $imgMarkup = '<picture> <source type="image/webp" srcset="..."> <img src="..." width=".." height="..." alt="..." srcset="..." sizes="..."> </picture>'; // If it is a ajax (HTMX) request, just echo the image markup and stop PW processing tyhe rest of the template. if ($config->ajax) { echo $imgMarkup; return $this->halt(); } /* When doing HTMX ajax swap, the rest of this template won't be rendered */ // Build the clickable thumbnails that do the swapping (simplified) foreach ($page->gallery as $item) { $thumbMarkup .= '<li><a href="..." hx-get="..."><img src="..." width="..." height="..." alt="..."></a></li>'; } } ?> <!-- The markup regions --> <pw-region pw-id="imageViewer"> <div class="image-wrapper"> <figure class="image-main-wrapper" id="mainImg"> <?=$imgMarkup?> </figure> <div class="image-thumbs-wrapper"> <!-- where the HTMX magic happens -- note the additional hx-headers, which allows PW's $config->ajax to work --> <ul class="image-thumbs" hx-trigger="click" hx-target="#mainImg" hx-swap="innerHTML" hx-headers='{"X-Requested-With": "XMLHttpRequest"}' > <?=$thumbMarkup?> </ul> </div> </div> </pw-region> <pw-region pw-id="pageContent" class="content-left">....</pw-region> <pw-region id="sidebarContent">...</pw-region> It just works... no need to faff with cancelling prepend/append templates etc, just rember to add the extra hx-header (also, you can do it once on a parent element), you don't need to add it to every call. I haven't ever tried, so I'm not 100%, but there might be a way to automatically add the header to every HTMX call with a bit of javascript in the header/footer. I feel all the tools are probably already there, but if there is an even easier way, though, that would be great.
    1 point
  18. This is imho a great video from the creator of Alpine.js:
    1 point
  19. Hi everyone, Here's a new module that I have been meaning to build for a long time. http://modules.processwire.com/modules/process-admin-actions/ https://github.com/adrianbj/ProcessAdminActions What does it do? Do you have a bunch of admin snippets laying around, or do you recreate from them from scratch every time you need them, or do you try to find where you saw them in the forums, or on the ProcessWire Recipes site? Admin Actions lets you quickly create actions in the admin that you can use over and over and even make available to your site editors (permissions for each action are assigned to roles separately so you have full control over who has access to which actions). Included Actions It comes bundled with several actions and I will be adding more over time (and hopefully I'll get some PRs from you guys too). You can browse and sort and if you have @tpr's Admin on Steroid's datatables filter feature, you can even filter based on the content of all columns. The headliner action included with the module is: PageTable To RepeaterMatrix which fully converts an existing (and populated) PageTable field to either a Repeater or RepeaterMatrix field. This is a huge timesaver if you have an existing site that makes heavy use of PageTable fields and you would like to give the clients the improved interface of RepeaterMatrix. Copy Content To Other Field This action copies the content from one field to another field on all pages that use the selected template. Copy Field Content To Other Page Copies the content from a field on one page to the same field on another page. Copy Repeater Items To Other Page Add the items from a Repeater field on one page to the same field on another page. Copy Table Field Rows To Other Page Add the rows from a Table field on one page to the same field on another page. Create Users Batcher Allows you to batch create users. This module requires the Email New User module and it should be configured to generate a password automatically. Delete Unused Fields Deletes fields that are not used by any templates. Delete Unused Templates Deletes templates that are not used by any pages. Email Batcher Lets you email multiple addresses at once. Field Set Or Search And Replace Set field values, or search and replace text in field values from a filtered selection of pages and fields. FTP Files to Page Add files/images from a folder to a selected page. Page Active Languages Batcher Lets you enable or disable active status of multiple languages on multiple pages at once. Page Manipulator Uses an InputfieldSelector to query pages and then allows batch actions on the matched pages. Page Table To Repeater Matrix Fully converts an existing (and populated) PageTable field to either a Repeater or RepeaterMatrix field. Template Fields Batcher Lets you add or remove multiple fields from multiple templates at once. Template Roles Batcher Lets you add or remove access permissions, for multiple roles and multiple templates at once. User Roles Permissions Batcher Lets you add or remove permissions for multiple roles, or roles for multiple users at once. Creating a New Action If you create a new action that you think others would find useful, please add it to the actions subfolder of this module and submit a PR. If you think it is only useful for you, place it in /site/templates/AdminActions/ so that it doesn't get lost on module updates. A new action file can be as simple as this: <?php namespace ProcessWire; class UnpublishAboutPage extends ProcessAdminActions { protected function executeAction() { $p = $this->pages->get('/about/'); $p->addStatus(Page::statusUnpublished); $p->save(); return true; } } Each action: class must extend "ProcessAdminActions" and the filename must match the class name and end in ".action.php" like: UnpublishAboutPage.action.php the action method must be: executeAction() As you can see there are only a few lines needed to wrap the actual API call, so it's really worth the small extra effort to make an action. Obviously that example action is not very useful. Here is another more useful one that is included with the module. It includes $description, $notes, and $author variables which are used in the module table selector interface. It also makes use of the defineOptions() method which builds the input fields used to gather the required options before running the action. <?php namespace ProcessWire; class DeleteUnusedFields extends ProcessAdminActions { protected $description = 'Deletes fields that are not used by any templates.'; protected $notes = 'Shows a list of unused fields with checkboxes to select those to delete.'; protected $author = 'Adrian Jones'; protected $authorLinks = array( 'pwforum' => '985-adrian', 'pwdirectory' => 'adrian-jones', 'github' => 'adrianbj', ); protected function defineOptions() { $fieldOptions = array(); foreach($this->fields as $field) { if ($field->flags & Field::flagSystem || $field->flags & Field::flagPermanent) continue; if(count($field->getFieldgroups()) === 0) $fieldOptions[$field->id] = $field->label ? $field->label . ' (' . $field->name . ')' : $field->name; } return array( array( 'name' => 'fields', 'label' => 'Fields', 'description' => 'Select the fields you want to delete', 'notes' => 'Note that all fields listed are not used by any templates and should therefore be safe to delete', 'type' => 'checkboxes', 'options' => $fieldOptions, 'required' => true ) ); } protected function executeAction($options) { $count = 0; foreach($options['fields'] as $field) { $f = $this->fields->get($field); $this->fields->delete($f); $count++; } $this->successMessage = $count . ' field' . _n('', 's', $count) . ' ' . _n('was', 'were', $count) . ' successfully deleted'; return true; } } This defineOptions() method builds input fields that look like this: Finally we use $options array in the executeAction() method to get the values entered into those options fields to run the API script to remove the checked fields. There is one additional method that I didn't outline called: checkRequirements() - you can see it in action in the PageTableToRepeaterMatrix action. You can use this to prevent the action from running if certain requirements are not met. At the end of the executeAction() method you can populate $this->successMessage, or $this->failureMessage which will be returned after the action has finished. Populating options via URL parameters You can also populate the option parameters via URL parameters. You should split multiple values with a “|” character. You can either just pre-populate options: http://mysite.dev/processwire/setup/admin-actions/options?action=TemplateFieldsBatcher&templates=29|56&fields=219&addOrRemove=add or you can execute immediately: http://mysite.dev/processwire/setup/admin-actions/execute?action=TemplateFieldsBatcher&templates=29|56&fields=219&addOrRemove=add Note the “options” vs “execute” as the last path before the parameters. Automatic Backup / Restore Before any action is executed, a full database backup is automatically made. You have a few options to run a restore if needed: Follow the Restore link that is presented after an action completes Use the "Restore" submenu: Setup > Admin Actions > Restore Move the restoredb.php file from the /site/assets/cache/AdminActions/ folder to the root of your site and load in the browser Manually restore using the AdminActionsBackup.sql file in the /site/assets/cache/AdminActions/ folder I think all these features make it very easy to create custom admin data manipulation methods that can be shared with others and executed using a simple interface without needing to build a full Process Module custom interface from scratch. I also hope it will reduce the barriers for new ProcessWire users to create custom admin functionality. Please let me know what you think, especially if you have ideas for improving the interface, or the way actions are defined.
    1 point
  20. I ended up turning this into a module, ProcessDatabaseRepair, though it also can also check and/or optimize the tables. So far works well, but since it interacts with database, i'd be worried about distributing the module. If anyone in particular needs such a module, let me know. It's quicker for me to use this to optimize the tables than to login to client's PhpMyAdmin and run it there; also if in the rare event described above, any table crashes, the hope is that this will fix it (yet to encounter a situation where it can be tested in that scenario, unless there is a way to force crash a table)...
    1 point
  21. Placing setlocale(LC_ALL, "en_US.UTF-8") in /site/config.php solved the issue for me, but I am not using the multi language capability. setlocale
    1 point
×
×
  • Create New...