Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 08/24/2022 in all areas

  1. 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.
    6 points
  2. You can either register your own api variable, eg in ready.php $wire->wire('contact', $yourpage); Or you simply add whatever property you need at runtime to the rockfrontend object: $rockfrontend->foo = 'Foo!'; $rockfrontend->bar = 'Bar!'; Then you can simply access those variables in your latte files: {$contact->title} {$rockfrontend->foo} {$rockfrontend->bar}
    3 points
  3. I think @fruid wrote a good short introduction above, he just missed a short "birds-eye view intro" explaining the bigger picture. I skimmed through the code of the module(s) and it is not that hard to see what the module does in general. If someone is really interested in it, then it is best to install it on a site that loads UI-Kit 3 and try in out. What @fruid could really add to his intro above is a short video, showcasing what the module does in action.
    2 points
  4. Ok, then to fix the error, in `ProcessLoginHistory` date format setting, put : Y-m-d H:i:s Then ping @teppo ?
    2 points
  5. Checkout for what? Checkout with which providers? Checkout with which framework? What is your module doing? Don't get me wrong but... a bit more details would be nice. Maybe adding your README here would be a great start. Is this based on Shopify, Snipcart, Foxy or plain ProcessWire? I'd like to play with your module. Easy one-click-solutions are always nice, but... details and some kind of introduction would be really nice. Update: Details were added.
    2 points
  6. Check and try the following, I think the link provided all the infos. /// get and mod inputfield submit $wire->addHookBefore('InputfieldSubmit::render', function($event) { /// get page being edited from from query string $id = (int) $this->input->get->id; // ⚠️ sanitize it, do not trust your xss ninja admins $page = $this->pages->get($id); /// i.e. exit if page name is not 'hello' if($page->name !== 'hello') return; /// get the object the event occurred on $submit = $event->object; /// make sure to target the `submit save` button (it could be more than one button) $name = $submit->attr('name'); if($name !== 'submit_save') return; /// change the button's label (attribute: value) $submit->attr("value", "Foobar"); }); /// mod the inputfield submit dropdown $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array actions /// get page being edited from event's object $page = $event->object->getPage(); /// i.e. exit if page name is not 'hello' if($page->name !== 'hello') return; $actions['to_the_moon'] = [ 'value' => 'to_the_moon', 'icon' => 'moon-o', 'label' => '%s + Go Moon ?', // Save + Go Moon 'class' => '', ]; $event->return = $actions; }); /// process the new action dropdown $wire->addHookAfter('ProcessPageEdit::processSubmitAction', function($event) { $action = $event->arguments(0); // action name, i.e. 'cache' if($action === 'to_the_moon') { $event->message("Your page was rocketed to the moon ??", "nogroup"); } }); /// remove all the dropdown action but keep our new one $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array of actions, indexed by name /* bd($actions) ? 'exit' => array 'view' => array 'add' => array 'next' => array */ /// remove all action, let just keep our `to the moon` action foreach($actions as $action) { $name = $action['value']; if($name === 'to_the_moon') continue; unset($actions[$name]); } $event->return = $actions; }); /// bonus - add a second button on the current edit page $this->addHook('ProcessPageEdit::buildForm', function($event) { /// get page being edited from from query string $id = (int) $this->input->get->id; // ⚠️ sanitize it, do not trust your xss ninja admins $page = $this->pages->get($id); /// i.e. exit if page name is not 'hello' if($page->name !== 'hello') return; /// quite self explanatory $href = $this->config->urls->admin."page/edit/?id=$id&to=the_moon"; $field = $this->modules->get('InputfieldButton'); $field->attr('id+name', 'input_moon'); $field->attr('class', $field->class); $field->attr('value', 'To the moon ?'); $field->attr('href', $href); $event->return = $event->return->append($field); });
    2 points
  7. Woah... there are a lot of moving parts here. First of all: It always takes way longer than anyone expected it would take. I have seen super simple and minimal projects taking weeks instead of days. Yet I've seen quite large and complex projects that took only a few weeks instead of months. In average the PHP/PW part is most of the time the smallest part within a project that's just dealing with content at least in my projects. It can be tricky when you have to deal with lots of data from different sources, especially when you do more than just displaying that data. Most time and energy in my projects go into: Planning out the whole project Designing each and every bit Content audit, creation, and optimisation Frontendwork HTML, CSS, JS Right after that ProcessWire comes into play with these parts: Templates and fields Site structure, setting up basic pages Modules and global settings Backend logic, data automation, cron/lazycron jobs, automated maintenance On average I'd say it's a 70/30 ratio between those two. To get you some numbers here: We talk about at least a few weeks and maybe even months in total with all kinds of related things like meetings, phone calls, organizing, setting up Google and other tools, like tracking tools, newsletter, etc. To make things clear and a bit more transparent, we talk about projects and websites like these: https://www.restaurant-blechnapf.de/ https://www.klippo-nms.de/ https://www.musikschule-neumuenster.de/ In comparison to the above there are projects I built in my spare time without a lot of planning or almost anything - which took a few hours or maybe two days in total. Like these: https://www.jet-fire.de/ https://www.voss-friseure.de/ The main difference is that the first 3 projects are totally custom and fully optimized, multiple user accounts, access rights, automation and maintenance and design-wise on a totally different level. While the last two use UIKIT or Bootstrap, just display simple content and aren't that well optimized to be honest. Last but not least there are projects like this: https://www.restaurants-neumuenster.de/ It started with a simple splash page, a simlpe form and a bit of content and went online within a few hours. Yet as of today there are several hundred hours of work in it. Quite a bit design (had a few relaunches in the past) but way more in terms of data processing, automation, newsletters and lot's of nice things. TL;DR From about 2 days to a couple of months.
    2 points
  8. A lot has changed since RockMigrations1. The new version is a lot easier to use! https://github.com/baumrock/RockMigrations RockMigrations was built for exactly that use case. I started in 04/2019 - so the module and its concepts have been developed, evaluated and improved for 3 years now and I'm using it in production for all of my websites. <?php namespace ProcessWire; /** @var RockMigrations $rm */ $rm = $this->wire->modules->get('RockMigrations'); // We make structural changes like adding a new page template on the local, $rm->createTemplate("your_new_template"); // adding a couple of fields in an existing one, $rm->setTemplateData("your_existing_template", [ 'fields' => [ 'title', 'body', 'add_existing_field1', 'add_existing_field2', ], ]); // changing settings on a field and whatnot, $rm->setFieldData('your_existing_field', [ 'label' => 'Your new field label', 'columnWidth' => 50, ]); Steep learning curve? I don't know... You have IntelliSense and it comes with migration snippets for VSCode and it comes with code inspector to copy and paste the syntax: # on local git push # on remote either login as superuser and reload the page # or for automated deployments just use the CLI: php /path/to/your/site/modules/RockMigrations/migrate.php
    2 points
  9. Oh, yes it's working now. That simple, I thought I'ts gonna take a lot more to fix this with a lot more trouble. ? Thank you very much, really appreciated ? R
    1 point
  10. Sure: I also tried module refresh with no luck. ? R
    1 point
  11. Ok then, first make a backup of the database, just in case, and please show us the full `call trace` logs shown on the tracy error (below the first error screen panel), along the ProcessWire version and MySQL version. You can also try to click on `Module Refresh` to see what happen. --- I didn't saw in first instance that the error was from the module `ProcessLoginHistory`, there must be a heck between the two modules. Will check. @Roych can you share the settings of `ProcessLoginHistory` and `ProcessLoginHistoryHooks` please ?
    1 point
  12. First I need... You're an hour ahead of me in France ?
    1 point
  13. To try it, you can just create a dummy page with page's name `hello` and paste the whole code in `ready.php` ?
    1 point
  14. Thanks @flydev ?? this is awesome. I've not even had a chance to try it out yet but I can see I had previously been doing something wrong... /// change the button's label (attribute: value) $submit->attr("value", "Foobar"); Had been trying to target 'label' rather than button value... duh! Probably led me down a blind alley from the get go!
    1 point
  15. @bernhard coming back to this as I just stumbled upon a scenario that was discussed here. In the Craft project I'm working on someone added changes that I needed to add to my setup. Got the message, tried applying and it stopped saying that these changes require the "Navigation" plugin. I added it (just a 'composer install' command line), came back, ran it again and done. The whole process feels very polished. I also had conflicts in the yaml files yesterday, two devs adding new fields. The way to fix it was to include both lines, and right at the beginning there's a timestamp that Craft uses to verify if it's up to date. Used the highest one and added 1 at the end to make sure everyone got an update check. It's really interesting to see how these yaml files are set. There's a main one that describes the project, with plugins, system stuff, some names and keys, and then there are individual files for each field, sections (templates), globals and other stuff that doesn't exist on PW.
    1 point
  16. I posted the fruits of my labour and your contributions in the post below, thanks to y'all, looking forward to your feedback!
    1 point
  17. Hi @wbmnfktr thanks for your message, I thought "checkout" is a term that speaks for itself. It's meant to be a shopping cart checkout and it doesn't use any third party software except for PayPal (for now). I edited my initial post, which is pretty much the readme.md file on github.
    1 point
  18. I'm sure you can do the same in gitea, we are using gitlab at work and it also works. The whole idea is that the deployment is done by php so you don't have to copy and paste complicated deployment scripts over and over again. And we can improve one script all the time instead of having 100 slightly different versions... But it's very opinionated of course. But works great for me and us.
    1 point
  19. Thanks for the report. It's hard to know when FormBuilder methods were added because there are no @since tags in the code. In v0.1.5 I've changed to older FB methods for better backwards compatibility.
    1 point
  20. @bernhard Trying this on a test project currently, I'm wondering if there's an easy way to add pages to RockFrontend the same way you're doing with $home. Let's say, I normally have a /contact/ page, stored in $contact, holding all possible information, phone numbers, social media links etc., and I'd like to make that page available in all rendered files. Right now, unless I'm missing something, I could use custom variables to pass to $rockfrontend->render(), but that doesn't seem to work with ->renderLayout().. The other minor issue I'm having, if you use Alfred on an empty text block, you don't get the edit links, the whole block just disappears. Other than that, very nice work, feels really good to be using Latte again ?
    1 point
  21. As we all know, Google's services are never free, we all pay by providing our own data to them: https://thehackernews.com/2022/01/german-court-rules-websites-embedding.html and we never know what they are using our data for, but this monkey business is quite profitable for them for sure.
    1 point
  22. When you search for getSubmitActions you will find some thread talking about adding and removing buttons. At some places you can see how titles/labels of those buttons were set. As a starting point: Maybe that helps? https://processwire.com/talk/search/?&q=getSubmitActions&quick=1&search_and_or=or&sortby=relevancy
    1 point
  23. Granted, I don’t have much experience with the complex column types (I only just updated after 10 years or so, to get the Page type), but if you primarily use primitive types, ProFields Table has the benefit of being a single SQL join away. So if you’re like me and you sometimes want to do custom queries, that’s pretty cool. Plus, while I have nothing against repeaters, for some things it just feels icky to have the Page overhead. I have a site with playlists, so there’s a table with Artist, Title and some auto-generated normalisation columns for search, for example. Another site manages ”trips“ using a column for the place and two DateTime columns for the duration. I think the need to connect multiple Page references to some metadata like dates is pretty common, and ProFields Table is perfect for that.
    1 point
  24. Works great as an alternative to repeaters. Way fewer pages. For example I use it on my ecommerce site to hold what items are in a person's cart.
    1 point
  25. @Mats @gebeer Thanks to a job I just finished for @dab I have been actively taking your work forward. I have a fork of the project here that... Simplifies getting the needed files used by MarkupLeafletMap into a template's header Integrates the Leaflet.AwesomeMarkers project and the latest FontAwesome icons into the module Adds a callback function for customising the content's of a marker's popup box Adds a callback function for customising the visual appearance of any marker Updates the readme file significantly Provides an example template file to help get folks underway with this module in their projects I based my work on gebeer's extension of your repo Mats, so I have issued a pull request to gebeer - but I'd like to get these changes into your repo if possible as then we can revert to your repo as the master codebase and, hopefully, have the latest goodies straight from the PW module directory. It's now very easy to add fields to the marker pages that let you customise their appearance. Below I have added an Options field, a FieldtypeFontIconPicker and a Text field to control the marker visuals via the added callback. If anyone want's to try it out, here is the link to the zip file.
    1 point
  26. I don't know the drawbacks of this, but I just managed to do make the dynamic stylesheet proposed on css tricks work has a pw template. So, this is how to do it (someone stop me if this is a bad bad thing to do): 1. Create a template file css.php and put all your css code inside. On the top of the file put this code <?php header("Content-type: text/css"); ?>. 2. Create a "css" PW template with that file. 3. Create a "css" page and give it the "css" template. 4. Link to the stylesheet like this <link rel="stylesheet" href="<?php echo $pages->get('/css/')->url; ?>">. 5. use the PW API on your stylesheet Extra: 6. put some fields on the "css" template and use them on your css Examples: the color picker field for colors; an image field with your style pictures (background, logo, etc). edit: onjegolders was faster! edit2: not the same answer. Mine is more interesting
    1 point
×
×
  • Create New...