Leaderboard
Popular Content
Showing content with the highest reputation on 08/26/2022 in all areas
-
This week we’ve got a new core version on the dev branch (v3.0.204) and a new version of FormBuilder (v53) posted in the FormBuilder board. Relative to ProcessWire 3.0.203 version 3.0.204 has 23 commits containing a mixture of feature improvements and issue resolutions. While there aren't major feature additions here there are a lot of worthwhile improvements and fixes, and I'd recommend the upgrade if using the dev branch. See the dev branch commit log for more details. The new FormBuilder version includes various improvements but the biggest is a new version of the included InputfieldFormBuilderFile module that supports file/image uploads in forms. It has been improved with a preview option (for images), the ability to collect a description for each file, improved multi-file inputs, and more. The new options appear on the “Details” tab when editing a file field in FormBuilder. This new version (v53) is available for download now in the FormBuilder board. If you upgrade an existing installation with file fields, I’d suggest testing out those forms after upgrade, and also review the new options to see if they make sense for your form. Thanks for reading and have a great weekend!12 points
-
The module provides a list of PageTable based content elements. It enables the backend user to easily create, publish, move, delete, copy and paste content elements. The rendering logic of the module is detached from the ProcessWire backend scope via Shadow DOM and allows the elements rendering identical to the front end. It ships with some helper functions that simplify the handling of content elements. Download/Install Github: https://github.com/neuerituale/PageTableNext Module directory: https://processwire.com/modules/page-table-next/ Composer: composer require nr/pagetablenext2 points
-
A dream comes true !!! There's a german saying: "Was lange währt wird endlich gut." (What lasts long finally becomes good.) Actually, I wanted to get off work and start the weekend, so I guess it will be nothing for the time being ?2 points
-
Hi @Neue Rituale Seems like a useful plugin. Thanks for sharing your work with the community. If there is a video to show how it works would be very useful. Gideon2 points
-
In this post we cover the details of a new module (ProcessLanguageFieldExportImport) that enables export and import capabilities for multi-language fields in ProcessWire— https://processwire.com/blog/posts/language-field-export-import/1 point
-
I don't know if you copy/pasted your code into your question, but three instances of a double quote on a line is rarely successful and you have that twice above.1 point
-
I should've put this topic into the Dev Talk forum section; my mistake! If a moderator wants to move it, by all means... ? I'm sure this isn't the most elegant way to do this, but it's what I came up with in the time I had. I ended up using a (ProField) Combo Field (see attached image for schema) to store each individual object's attributes that I wanted to have some level of editability within the CMS, and then stored those in a simple Repeater field as part of the "floorplan" template. I'll lock down the class_name, class_addition, and svg_path fields to administrator level accounts as they will be maintained in a much more fragile way. I used a Pages::saveReady hook in ready.php to detect changes on the repeater template (all repeaters have a hidden system template) in order to read in a version of the floor plan SVG that had valid, but easily identifiable XML strings to be used for (dynamic) replacement - and then write out a modified version using said replacement. An example of that string is below -- the floor number is the only thing that differs between each {placeholder}-esque string. <g id="floor0_fill"></g> The hook would then loop through all of the data provided in each combo field to generate the layers as needed, and then save the new version of the SVG file, completed as expected. I'll provide the code below, but it's only intended as an example for what I did. // Floor plan page - dynamically generate the static SVG from a template, and saved page fields' data $wire->addHookBefore('Pages::saveReady', function(HookEvent $event) { // Get values of arguments sent to hook (and optionally modify them) $page = $event->arguments(0); if ($page->template != 'floorplan') return; // Only process if the content of the floorplan items has changed $changes = $page->getChanges(true); if (!isset($changes['floorplan_repeater'])) return; // Generate the individual item SVG element entries $repeater = $page->floorplan_repeater; $svg = []; foreach ($repeater as $item) { $name = $item->floorplan_item->name; $type = implode(' ', $item->floorplan_item->type); $path = str_replace("\n", "\n\t\t\t\t", $item->floorplan_item->svg_path); $level = $item->floorplan_item->level - 1; // fix offset as set from PW ComboField's Select type $class = $item->floorplan_item->class_name; $full_class = $item->floorplan_item->class_addition ? "{$class} {$item->floorplan_item->class_addition}" : $class; $svg[$level][] = " <g id='floor{$level}_{$class}' class='{$type} {$full_class}'> <a class='item' xlink:href='#content-skip'> {$path} <desc>{$name}</desc> <text class='label' x='40' y='70'>{$name}</text> </a> </g>"; } // Open the SVG template (floorplan-template.svg), replace the content where needed, and save to the final SVG file (floorplan.svg) $path = $this->wire()->config->path('templates') . 'svgs/'; $svg_template = $this->wire('files')->fileGetContents("{$path}floorplan-template.svg"); if ($svg_template === false) { $this->error("The SVG template file could not be found: \"{$path}floorplan-template.svg\"", Notice::log); return; } foreach ($svg as $index => $floor) { $svg_template = str_replace('<g id="floor'.$index.'_fill"></g>', implode("\n", $floor), $svg_template); } $this->wire('files')->filePutContents($path.'floorplan.svg', $svg_template); }); It took me quite some time to find the proper way to identify a file path in ready.php!! That alone might be useful to someone. PW's filePutContents method (which also sets chmod) needed a path formatted in a specific way before it would work, and I wanted to do it the PW way, rather than the standard PHP way. Anyway, moving on... At this point all of the data was in the database, the SVG was generated dynamically from a template, and I moved on to dynamically generating the hidden definition list elements (that provide directions to each selectable object on the map, per floor), and the form elements that filter which map object is being highlighted on the map (with directions) at any given time. Lots of class/id/data massaging trickery, for sure! ...like the one below which was especially fun (and extremely ugly code). // Example Output: <span class="badge">G</span> <span class="badge">2</span> // Example Input: [0 => 'ground', 2 => 'two'] $badges = '<span class="badge">' . implode('</span> <span class="badge">', explode(',', str_replace('0', 'G', implode(',',array_keys($properties['floors']))))) . '</span> '; ? It's all working as of today, I only have some cleanup and optimizations to do - and the staff have just decided to do major furniture changes. ?♂️ At least the coding part is done! Back to the drawing board on the Illustrator file though!1 point
-
It’s quite difficult to debug code from a few snippets (maybe someone smarter than me can…). I suggest you install the TracyDebugger module and insert some bd() statements to see what is going wrong.1 point
-
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
-
Maybe this can be helpful ========================================================================================= $page->images->first(); // first image $page->images->last(); // last image $page->images->eq(0); // first image $page->images->eq(1); // second image ========================================================================================= Find the total number of pictures // Get the page that holds the the pictures $p = $pages->get('/path_to_page/'); $numberofimages = $p->images->count(); echo $numberofimages; ========================================================================================= images is an array ... see all the array elements inside the array images 1) Get a Page who has images $p = $pages->get('/path_to_page/'); 2) this gets all the elements $array = $p->images->getKeys(); 3) and this outputs to see them echo implode($array); ========================================================================================= Because an array starts with item number 0 and not 1 ... the last item number in the array is one less than total counted // echo $n; show the total counted number // echo $n--; show the total counted number minus 1 ... this is the last item in the array Note that when you upload an image in the backend by default it will be the LAST one added If you want to output the images in a gallery on the front and want the latest uploaded image to be the first in the gallery, you have to reverse the backend image order: Example: $var_page = $pages->get('/path_to_your_page/'); foreach($var_page->images->reverse() as $image) { echo "<a href='$image->link'><img src='$image->url' alt='description' width='' height=''></a>"; } ========================================================================================= F.1 point
-
@Roych You can clear the copy/paste memory in the select options, but yeah I think the memory could clear itself after for example loggin out. I think no one needs something forever in the memory. ?1 point
-
Have not installed it yet - but this module looks promising at first sight. I am looking for a super-simple solution for selling vouchers on sites. A big shop module like Padloper would be way to oversized for this task.1 point
-
This behaviour indicates that something is wrong with the .htaccess file in the root folder. Please check if the file is present in the root folder of the site, if not, you can get a copy here: https://github.com/processwire/processwire/blob/master/htaccess.txt - download this file and then rename it to .htaccess.1 point
-
Just a thought before we go further. When is the 404 page displayed? On the index page (frontend) or only the admin page? If it's the second case, the .htaccess file is probably missing or the admin page entered may not be the right one (this can be checked in the database, table pages, id 2, column name) Otherwise, in the first case, check that you uploaded all the required processwire files (at least the index.php which will throw an error if the setup is ok, or as @DV-JF double or triple check your apache configuration on xampp.1 point
-
I think the issue is, like I mentioned before, that many devs don't want their module to rely on yet another third party module to function (install/uninstall) correctly. What if the module is discontinued some day or outdated? What you could do, just an idea, is make the migrate module serve the same purposes it does already, but instead of offering methods, generate the code that would work with pure PW that developers can put in their module. Also, not sure if that's possible or this or another module offers that already, allow for fields and template export. So what happens a lot to me at least is, I work on a site, create some fields and templates via GUI and when I'm happy I want these fields in another installation or better yet (from now on at least) included in a module. Then I would need to re-overthink what I did via GUI to accomplish the same thing via API. If you could just use RockMigrations, navigate to the field(s), template(s) or even page(s)? in question and click click bing bang boom it shows you the API-code you need for PW to install it in anywhere else.1 point
-
Likely never. I know how great it works and how much difference it can make. But it seems that there are very few people actually using it. Even though it's free and one of the best things I have built over the last few years. The reason why it is free is mostly because I thought many other module developers could benefit a lot from using it for their modules. We could have blog modules that create all the necessary fields and templates for example. I guess you'd be 10 times quicker using RockMigrations compared to a traditional API based setup. I plan to create a video about it as soon as I can. Maybe that will open some eyes. Maybe it will also show Ryan the benefit of the concepts and we get something similar for PW4 one day, who knows...1 point
-
Interesting problem. If I understand correctly, you want to have two sources of truth that you need to connect somehow, i.e. the data inside the SVG and data from ProcessWire? I wonder if that’s worthwhile, because what are the chances you’ll want to change something on one side and not the other? To get translatable strings inside the SVG you could always use {placeholders} and populate them with WireTextTools, but as soon as you significantly change a text’s length, you’ll need to touch the SVG anyway, plus you will need to keep the placeholder names in sync. Obviously authoring the SVG is the most annoying part of managing this content, but I don’t see how you would get around it, so maybe try and make it the only part? You could cram everything into the SVG in some standardized way, like predefined classes and data- attributes, then analyze it with JS and build all the toggles dynamically.1 point
-
I think a couple of ways are possible. One is similar to what @horst suggested, but you can use the special created timestamp of 10 to more exactly identify temporary images: $page->getUnformatted('images')->find('created!=10') There are also the public Pagefile::isTemp() and Pagefiles::isTemp() methods that can be used to check if a particular Pagefile/Pageimage is temporary or not. Another way would be to set the outputFormat property of the images field to array before you get the field value so that the value is standardised for single and multiple image fields. $page->getField('image')->outputFormat = FieldtypeFile::outputFormatArray; // Now use $page->image1 point