Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/03/2022 in all areas

  1. This week on the core dev branch we’ve got some major refactoring in the Page class. Unless I broke anything in the process, it should be more efficient and use less memory than before. But a few of useful new methods for getting page fields were added in the process. (A couple of these were also to answer feature request #453). Dot syntax You may have heard of dot-syntax for getting page fields, such as $pages->get('parent.title') where “parent” can be any field and “title” can be any field that has subfields. This is something that ProcessWire has supported for a long time, but it doesn’t get used much because it was disabled when output formatting was on. So it wasn’t something you could really count on always being there. Now you can — it is enabled all of the time. But it’s also been rewritten to be more powerful. When using dot syntax with a multi-value field (i.e. any kind of WireArray value) you can also specify field_name.first to get just the first value or field_name.last to get just the last value. i.e. $page->get('categories.first'); will take a value that was going to be a PageArray (‘categories’) and return just the first Page from it. Bracket syntax With bracket syntax you can call $page->get('field_name[]') with the (‘[]’ brackets at the end) and it will always return the appropriate array value for the type, whether a PageArray, WireArray, Pagefiles/Pageimages, or regular PHP array, etc. This is useful in cases where you know you’ll want a value you can foreach/iterate. Maybe you’ve got a Page field that set set to contain just 1 page, or maybe you’ve got a File/Image field set to contain just 1 file. But you want some way to treat all of your page or file/image fields the same, just append “[]” to the field name in your $page->get() call and you’ll always get an array-type value, regardless of the field settings. This bracket syntax can also be used for getting 1 value by index number. Let’s say you’ve got a page field named “categories” that contains multiple pages. If you want to get just the first, you can just call $page->get('categories[0]'); If you want to get the second, you can do $page->get('categories[1]'); This works whether the field is set to contain just one value or many values. Using the first index [0] is a good way to ensure you get 1 item when you may not know whether the field is a single-value or multi-value field. Another thing you can do with the bracket syntax is put a selector in it to filter a multi-value field right in the $page->get() call. Let’s say you want all categories that have the word “design” in the name. You can call $page->get('categories[title%=design]'); If you want just the first, then $page->get('categories[title%=design][0]'); What’s useful about using selectors in brackets is that this does a filter at the database-level rather than loading the entire ‘categories’ field in memory and then filtering it. Meaning it's a lot more memory efficient than doing a $page->get('categories')->find('title%=design'); In this way, it’s similar to the already-supported option to use a field name as a method call, for instance ProcessWire supports $page->field_name('selector'); to achieve a similar result. Dot syntax and bracket syntax together You can use all of these features together. Here’s a few examples from the updated $page->get() phpdocs: // get value guaranteed to be iterable (array, WireArray, or derived) $images = $page->get('image[]'); // Pageimages $categories = $page->get('category[]'); // PageArray // get item by position/index, returns 1 item whether field is single or multi value $file = $page->get('files[0]'); // get first file (or null if files is empty) $file = $page->get('files.first'); // same as above $file = $page->get('files.last'); // get last file $file = $page->get('files[1]'); // get 2nd file (or null if there isn't one) // get titles from Page reference field categories in an array $titles = $page->get('categories.title'); // array of titles $title = $page->get('categories[0].title'); // string of just first title // you can also use a selector in [brackets] for a filtered value // example: get categories with titles matching text 'design' $categories = $page->get('categories[title%=design]'); // PageArray $category = $page->get('categories[title%=design][0]'); // Page or null $titles = $page->get('categories[title%=design].title'); // array of strings $title = $page->get('categories[title%=design].title[0]'); // string or null // remember curly brackets? You can use dot syntax in there too… echo $page->get('Page “{title}” has {categories.count} total categories'); I’m not going to bump the version number this week because a lot of code was updated or added and I’d like to test it for another week before bumping the version number (since it triggers the upgrades module to notify people). But if you decide to upgrade now, please let me know how it works for you or if you run into any issues. Thanks for reading, have a great weekend!
    6 points
  2. I have setup a starter kit for developing PW sites with Tailwind CSS. You can find it at https://github.com/gebeer/tailwind-css-webpack-processwire-starter I know, I'm a bit late to the party with Tailwind. In fact I only discovered it a couple of months ago and found it to be a very interesting concept. It took quite some time to get a pure webpack/postcss/tailwind setup that features live reloading dev server, cache busting, supports babel as well as autoprefixing and works well with PW. For the cache busting part I developed a small helper module that utilizes webpack.manifest.json to always load the assets with the correct file hashes for cache busting. No more timestamps, version numbers or the like required. The little helper can be found at https://github.com/gebeer/WebpackAssets Having used it for the first time in production on a client project I have to say that I really enjoy working with Tailwind. The concept of using utility classes only really convinced me after having put it to practice. During the work on the client project, some quirks with webpack-dev-server came to surface. I was able to solve them with the help of a colleague and now it is running quite stable in both Linux and Mac environments. I am fascinated by the fast build times compared to using webpack/gulp or gulp/sass. Also the small file size of the compiled CSS is remarkable compared to other frameworks. So this will be my goto setup whenever I am free to choose the frontend framework for a project. If anyone feels like they want to give it a try, I shall be happy to get your feedback
    1 point
  3. @ryan a possible solution for future enhancements to MarkupRegions is that only comments starting with <!--# are the ones that will be removed in the process and the original html comments will be kept. @gebeer you can try to make that modification in your copy of the code.
    1 point
  4. Those permissions only allow access to see the Setup > Admin Actions menu. In the other thread I mentioned that you need to give roles access to each specific action. I am still not clear if you have done that or not. I am referring to the interface below in the module's settings. Can you confirm you have given access to your custom action?
    1 point
  5. I think the cheatsheet is here. Maybe just change the link in the original post? I remember starting my way with this cheatsheet. I learned a lot. Not sure if it is up to date, but even as is it could be of a great value to many. Captain hook is also available, just moved to github.io
    1 point
  6. Unfortunately this is not working. And it looks ugly and feels so wrong ;-) But thank you for the suggestion.
    1 point
  7. Ryan was quick with this one ? It's now easy and bullet proof: <?php $img = $page->get('images.first'); if($img) echo "<img src={$img->size(200,200)->url} alt=...>"; https://processwire.com/talk/topic/27528-weekly-update-– 2-september-2022/#comment-226391
    1 point
  8. With document.ready I was referring to the javascript event that is fired when the DOM is ready. See here: https://stackoverflow.com/a/9899701/6370411 That has nothing to do with the server side ready.php file ? That's quite easy to do: <script> let infoShown = localStorage.getItem('medicalPopup'); if(!infoShown) { UIkit.modal(...).show(); localStorage.setItem('medicalPopup', 1); } </script>
    1 point
  9. Your examples are confusing. Your first snippet shows UIkit.modal().show() just before </body> and the second shows that you are loading the script just before </body>. So which one is true? The loading order is important, so if you do that: <script src="uikit.min.js"></script> <script>console.log(UIkit)</script> You should get a proper log in the devtools of your browser, but if you do that: <script>console.log(UIkit)</script> <script src="uikit.min.js"></script> You'll get "undefined" for your UIkit variable. So if you did UIkit.modal(...).show() in the second example you'd get the error that you described. What you can also do is to wrap your javascript in a document.ready callback: <script> document.addEventListener("DOMContentLoaded", function(event) { console.log(UIkit); }); </script>
    1 point
  10. Hi! I'm a new member here, sooo, nice to meet you all :)
    1 point
  11. I couldn't find anything in the documentation, but in the core file it seems that comments starting with <!--# don't get stripped out: https://github.com/processwire/processwire/blob/3acd7709c1cfc1817579db00c2f608235bdfb1e7/wire/core/WireMarkupRegions.php#L844 <!--# Comment --> I tried it and it seems to work. Regards, Andreas
    1 point
  12. This IS NOT related directly to ProcessWire code. A critical CVE vulnerability was found in ZLib. Posting here in case some of you manage dedicated servers, and/or for checking if your hosting provider(s) do their job. more information: https://nvd.nist.gov/vuln/detail/CVE-2022-37434
    1 point
  13. Here's my most recent project: https://velhinhos.pt/ Velhinhos (translates to something like "little old people") is a free service that helps people understand their needs and find the best care for their elderly, be it nursing homes, at-home services or daycare. The branding – name, logo, tone – is also our work, and we're providing continuity support on ads management and social media. This site features some information/advice about the available options, a blog, and a quiz that helps users better understand the level of dependency. It also feeds leads to their Pipedrive CRM. The frontend is a mix of Tailwind and StencilJS.
    1 point
  14. I'm working on a one-page, vanilla javascript and php (PW API) shopping checkout module that integrates PayPal server side. Here's version 1.0 https://github.com/dtjngl/kiosk Looking forward to your feedback! DISCLAIMER this is a BETA version at best, it's my first "real" module I'm writing for ProcessWire (and first module I'm writing at all) so please don't eat me alive. I'm aware that it needs a lot of work, but at this point, but it's functional. I'm also aware that it is not very customisable at this point (it's mostly customised for one specific use case), but I promise I will work on that. My main objective is to get more seasoned developers' mentoring, feedback, advice, warnings – and and of course, if you want to look into or help develop it further – pull requests. Please point out any potential dangers you see or anything that raises a red flag for you. I'm learning so much from that. Though the module is held in german, you can translate almost all strings that are in the php files via PW's translate functionality. I cannot help with the strings in the javascript yet, but will do so soon (using a json file that lives on the file system probably). I started working on this module because many checkout systems out there are either overkill and too complicated, didn't fulfil my needs or come with a premium fee that is not worth it. Also, austrian law (and other countries' jurisdiction) demand a different flow than what PayPal's Express checkout offers. So this version makes calls to PayPal on the server. WHAT IT CONTAINS - OnePage checkout system with 4 steps: cart, address(es) form, payment method (paypal and deferred), order overview - "minicart" (the cart-summary in the page's header) - buy button (to add items to the cart) - a dashboard (with admin template) for an overview of all placed orders uses server session storage and browser localstorage REQUIRED WireMail SMTP UIKIT (for best behaviour, will be optional soon) vanilla Javascript (no jQuery required) PHP (DUHDOY!) ProcessWire Version (not sure, but works with version 3.0.200) if you want it in english you need a multi-language installation PayPal Business Account (Live or Sandbox to test with) HOW TO INSTALL 1. Put the folder "Kiosk" in your ProcessWire site's modules folder 2. Install the module Kiosk, this will… - create fields for the order template - create a template for the orders placed - create a template for the orders' parent page - create a parent page "Custom Orders" for all orders under /admin/page/ (you can rename the page, not the template) - create a template "kiosk_checkout" for the checkout page - create a page "Checkout" for the checkout, child to "Home" (you can move the page, you can rename the page, not the template) - install module ProcessKiosk along with it - ProcessKiosk create a page under /admin/ (visible in the module upon creation). Here you will see a table with all the orders placed. 3. check the Kiosk module's settings page (navigate via the module's overview page) and enter details to your best knowledge. Some are not "required" but they actually are. 4. Put the below code (in essence) on "kiosk_checkout.php": if ($config->ajax) { $kiosk->handleAJAX($input); return $this->halt(); } else { $kiosk->handleStaticContent($input); echo $kiosk->renderCheckoutSteps(); } 5. put this line on a page that loads everywhere, preferably on init.php: $kiosk = $modules->get('Kiosk') 6. put this line where you want the "minicart" and toggling cart preview (provided the site runs UIKIT) to be, probably in a header that renders on each page: echo $kiosk->renderMiniCart(); 7. put this line just above you closing tag: echo $kiosk->addScripts(); HOW DOES THIS THE ORDERING WORK? 1. Add items to the cart or update quantity when viewing the cart, continue with "weiter". 2. Enter your address, if your billing address differs, uncheck the "gleiche Rechnungsadresse" and enter the billing address, checked or unchecked, both forms will be sent to the server but handled accordingly, continue with "weiter" 3. Select a payment method, continue with "weiter" 4. formdata object (containing cart items, address(es), payment method) will be sent via AJAX to the server and stored in the server session variable 5.1. if payment method is deferred - AJAX response contains the order summary markup that will render in step4 - don't forget to check " ich habe die Datenschutzerklärung sowie die AGB zur Kenntnis genommen und akzeptiere diese." (privacy policy and terms and conditions) - click on "Zahlungspflichtig bestellen" will send another AJAX request to the server thus submitting the order (continue to WHAT HAPPENS WHEN AN ORDER IS PLACED?) 5.2. if it's paypal, a bit more is happening - server sends a cURL request to paypal containing client ID and secret - response will send a token - server sends that token along with the purchase unit (created from our placed order) in another cURL request to paypal - response will send an "approve"-URL - AJAX response contains that URL - user is redirected to paypal to approve the order - user is redirected to the "checkout" page along with a token and PayerID as GET parameters - token (not needed actually) and PayerID are stored in the server session - with the PayerID in the session variable and the "status" of the paypal approved order in the localstorage the checkout process will head on to step 4: order summary - don't forget to check " ich habe die Datenschutzerklärung sowie die AGB zur Kenntnis genommen und akzeptiere diese." (privacy policy and terms and conditions) - clicking on "Zahlungspflichtig bestellen" will send another AJAX request to the server - second AJAX request will send PayPalAccessToken, PayPalPayerId and PayPalRequestId in another cURL to PayPal which will trigger the payment - response will… continue to WHAT HAPPENS WHEN AN ORDER IS PLACED? WHAT HAPPENS WHEN AN ORDER IS PLACED? an order-page with all the order's details (plus order number from the kiosk's number circle) is created under /admin/page/custom_orders/ (you can find it in the Kiosk Dashboard) number circle is iterated email markup is created using the module's default email template, you can add a path to a custom template in the module's settings email is sent to the site master and the user (check the module's settings for email addresses etc.) order in server session is reset to an empty array paypal session in server session is reset to an empty array localstorage of the browser is deleted user is redirected to a custom url (defined in the module's settings) HOW ARE PRICES HANDLED? prices allow for a stack price functionality. This means that prices and shipping depend on the quantity of items purchased. You can enter different prices and shipping costs for different quantities. If a user's amount of a selected item reaches the specified stack price, the item price and the shipping costs change. HOW ARE PRODUCTS ADDED TO THE CART? a product should be a page object to keep things simple. That page needs an "images" array field (that very name). Below you can see what a product would need. These values are added to the button's data attributes. // This is how you make a product… $product = new WireArray(); $product->id = $page->id; $product->title = $page->title; $product->source = $page->parent->name; $product->url = $page->url; $product->images = $page->images; $product->product_width = 150 // for proportional item image calculation in the cart $product->taxrate = 10 // or what ever the tax rate $product->stack_prices = array( array( "qu" => 1, "sp" => 19.99, "sh" => 5 ), array( "qu" => 10, "sp" => 14.99, "sh" => 0 ), ); $product->stack_prices = htmlspecialchars(json_encode($product->stack_prices)); // then render the "add to cart" button $kiosk->renderBuyButton($product); UPDATE version 1.1: installing kiosk.module will also create a repeater field "kiosk_product_stack_prices" (including 3 subfields as described above) that handles the stack_prices array of arrays (as described above) so you can use the GUI. create a field "kiosk_product_tax_rate" create a field "kiosk_product_width " create a field "images" if there is none in your system. create a template "kiosk_product" with all these mentioned fields use this template for your products (or the fields thereon at least and add to your template of choice) and you should be good to go. CAUTION! If you uninstall kiosk.module it will delete all pages with template "kiosk_product", that's because I'm still figuring out how to detach the fields and fieldgroups without deleting the template. Also, if you're coming from version 1.0 and want to upgrade, please uninstall it in 1.0 first and only then get the new repo (v1.1) to install again. Class kiosk now provides the following hookable methods: ___renderBuyButton(Page $product, (string) 'add to cart') pass a product (preferably with template kiosk_product) and it returns a buybutton with the price for 1 item. This method now accepts the button's label (string) as a second argument. Method is hookable. ___getSpecificStackPrice(Page $product, (int) 5) // 5 is just an example pass a product (preferably with template kiosk_product) and an amount, it returns the price for that amount of items. Method is hookable. ___getSinglePrice(Page $product) pass a product (preferably with template kiosk_product) and it returns the price for 1 item. Method is hookable. So, use template "kiosk_product" for your products, edit the product page to add details, and then do echo $kiosk->renderBuyButton($product, (string) 'add to cart'); to render an "add to cart" button.
    1 point
×
×
  • Create New...