Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by eelkenet

  1. Did you check the database to see if the table is in fact still there?
  2. It depends completely on the payment provider you choose, there are several modules available, but they all require some hands-on programming. http://modules.processwire.com/search/?q=payment Also often payment providers require a callback-endpoint at your site to process the result of a payment action. If all goes well, it will call this endpoint while the user is waiting for a bit during the payment process. After your system processes and acknowledges the payment (for instance it changes the status of the user to 'registered'), the payment provide will then forward the user back to your site. But.. it can also be the case that this callback happens minutes, hours or even days later in case of a hiccup with the banking system for instance. And, in case of a credit-card payment, it can in fact even change the state of a payment a month later — should the user formally dispute the charge. So, the flow of registration -> payment -> access is not as 1-2-3 as it may seem. You will need to consider all the possible outcomes of your registration process. You will be able to read more about the exact information your payment provider sends and expects in return in their documentation, as this varies wildly. Another thing to consider is that you cannot depend on any $session variable during the callback function, as the payment provider has its own session. So you will probably have to pass the user-id to the payment provider while setting up the payment, which it in return should include during its callback to your server and which you can then use to look up the user. One other thing though, just in case you hadn't thought about this: you should never email users their passwords. With the exception of a single-use, time-constrained temporary password. For that purpose you can use the PasswordForceChange module.
  3. Thank you once again @Robin S, that is very helpful indeed. The other settings were mostly the result of me fiddling around trying to get the field back to working 👨‍💻💣⁉️☠️ I think it would be very helpful if this was clearly explained in the formatText's notes, as it is easy to overlook.
  4. So, this is a weird one. I initially thought this was a "CKEditor inside of Repeater Matrix"-problem, but for some reason a certain set of settings makes the CKEditor crash upon loading. I've tried on multiple systems, multiple browsers, and both on 3.0.123 (master) and the latest 3.0.142-beta. I'm probably making a simple mistake here, but couldn't figure it out. I've worked around the issue for now, but leaving this here for other to take a look at. To replicate: Import the following field, add it to a page, and try to edit. { "simple_body": { "id": 224, "name": "simple_body", "label": "Rich text with limited options", "flags": 0, "type": "FieldtypeTextareaLanguage", "inputfieldClass": "InputfieldCKEditor", "contentType": 1, "htmlOptions": [ 2 ], "minlength": 0, "maxlength": 0, "showCount": 0, "rows": 10, "toolbar": "NumberedList, BulletedList, PWLink, Unlink, SpecialChar, Sourcedialog", "inlineMode": 0, "useACF": 1, "usePurifier": 1, "toggles": [ 2, 4 ], "formatTags": "p;ul;li;a", "extraPlugins": [ "pwlink", "sourcedialog" ], "removePlugins": "image,magicline", "langBlankInherit": 0, "collapsed": 0, "textformatters": [ "TextformatterEntities" ], "showIf": "", "themeOffset": "", "themeBorder": "", "themeColor": "", "columnWidth": 100, "required": "", "requiredAttr": "", "requiredIf": "", "imageFields": "", "extraAllowedContent": "", "contentsCss": "", "contentsInlineCss": "", "stylesSet": "", "customOptions": "", "plugin_sourcedialog": "" } }
  5. Thank you very much @Robin S, I have been able to fix the issue! 🙂
  6. I just found out my fancy Float (Range) slider does not work with Repeaters, unless I disable AJAX for the repeater. This is because I pass the Inputfield's settings to the JS side of it through the $config->js route. For every field I use the unique settings of that field. On the InputField I set these accordingly: $otherFields = $this->config->js('InputfieldFloatRange') ? $this->config->js('InputfieldFloatRange') : []; $this->config->js('InputfieldFloatRange', array_merge($otherFields, [ "Inputfield_" . $this->attr("name") => [ 'precision' => $this->get("precision"), 'rounding' => $this->get("roundingMethod"), 'displayValueField' => $this->get('displayValueField'), 'min' => $this->attr("min"), 'max' => $this->attr("max"), 'step' => (float) $this->attr("step") ] ])); And retrieve them on the Javascript side: function getSettings(id) { return window.ProcessWire.config.InputfieldFloatRange[id]; } var settings = getSettings(event.target.id); // from a 'change' event on the slider However, when I create a new repeater item, it does not append that items slider's settings to the ProcessWire.config object, as I would have expected. I could use an alternative path (using data-attributes in the actual field), but would prefer to keep this neatly. So, does anybody know what I need to do to append my Inputfield's settings to $config->js dynamically, ie. upon creating a new repeater item (with AJAX)?
  7. The module has been updated to 004 005. Besides a small JS bugfix, this update makes the rounding of manually entered values configurable (floor, round, ceil or disable completely). Edit: As of version 005 it also works within repeaters / repeater matrix, thanks to @Robin S' help.
  8. Hi @klikrzys. What does it say when you print the entire query-string? https://processwire.com/api/ref/wire-input/query-string/
  9. I can confirm, this issue also appears on my installation. It's easy to miss unless you change your own profile language. Besides, I found a another minor bug: switching all multi-language input fields at once (double click on language-tabs) closes the image field. Reproduction is easy: With an image field in the regular grid mode and an image opened: double click any language-tab inside the custom fields template (to change all fields to that language). This switches languages, but then immediately also closes the image field. In this case the javascript event should not propagate any further.
  10. To be honest I didn't check out that module in a long time, and had even forgotten it existed.. 😅 But the major differences would be FieldtypeRangeSlider is a full-fledged module with its own API, depends on jQueryUI, and.. has not been updated in 5 years. The one I created is much simpler and light-weight: it's really just a basic HTML5 <input type='range'> with some styling, feedback and validation. As such, it is just another way to use the regular Float/Integer inputfields.
  11. Hi! I've created a small Inputfield module called InputfieldFloatRange which allows you to use an HTML5 <input type="range" ../> slider as an InputField. I needed something like this for a project where the client needs to be able to tweak this value more based on 'a feeling' than just entering a boring old number. Maybe more people can use this so I'm hereby releasing it into the wild. EDIT: You can now install it directly from the Modules directory: http://modules.processwire.com/modules/inputfield-float-range/ What is it? The missing range slider Inputfield for Processwire. What does it do? This module extends InputfieldFloat and allows you to use HTML5 range sliders for number fields in your templates. It includes a visible and editable value field, to override/tweak the value if required. Features Min/max values Precision (number of decimals) Optional step value (Read more) Optional manual override of the selected value (will still adhere to the rules above) Configurable rounding of manually entered values (floor, round, ceil, disable) Usage Clone / zip repo Install FieldtypeFloatRange, this automatically installs the Inputfield Create new field of type `Float (range)` or convert an existing `Float`, `Integer` or `Text` field. To render the field's value simply echo `$page->field` Demo A field with Min=0, Max=1, Step=0.2, Precision=2 Field with settings Min=0, Max=200, Step=0.25, Precision=2 Todo Make the display-field's size configurable (will use the Input Size field setting) Hopefully become redundant Changelog 005 (current version) - Fix bug where the Inputfield would not work properly within repeaters / repeater matrices 004 - Make rounding of manually entered values configurable (floor, round, ceil or disable) - Fix small JS bug when the value-display field was not displayed - Update README 003 - Code cleanup, add some ModuleInfo data & LICENSE - Submit to PW Modules directory (http://modules.processwire.com/modules/inputfield-float-range/) 002 - Fix issue where setting the step value to an empty value created problem with validation - Make the display-field optional 001 - Initial release Thanks!
  12. I ran into issues with a slow server using this method, where wireHttp would already resolve a result while RestApi was still running. So with help from my co-worker @harmvandeven we came up with a more stable version, with some more redundancy: <?php /* api.php, a simple PW template to fill the gap between the RestApi and ProCache modules v2 @author: @eelkenet, with help from @harmvandeven & @ryan 1. Create a template called 'api' and set it up using the following settings: - Allow URL segments - Allow guests to view the page - Set the Content-Type to application/json - Make sure to NOT Prepend or Append any files 2. Add a page using this template and allow ProCache to run its magic */ $timeout = 600; $maxAttempts = 10; // Pre-check for unwanted symbols if (strpos($input->urlSegmentStr(), '.') !== false) { throw new Wire404Exception(); } // Build request URL $endpoint = $modules->get("RestApi")->endpoint; $url = $input->scheme() . "://" . $config->httpHost . "/" . $endpoint ."/" . $input->urlSegmentStr(); $http = new WireHttp(); // Set a high timeout, to deal with a slow server $http->setTimeout($timeout); // Get the HTTP status of the page, to make sure it exists in the first place $status = $http->status($url); // If the page exists, or possibly redirects to valid content if ($status >= 200 && $status < 400) { $result = false; $attempt = 0; // If the result isn't a string, something went wrong while(gettype($result) !== "string" && $attempt++ < $maxAttempts) { $result = $http->get($url); if ($attempt > 1) wire()->log->message("Loading content at $url, attempt $attempt: " . gettype($result)); } // Double check if the data is a string.. if (gettype($result) === "string"){ // .. And check if it can be decoded, if so: return the data and thus cache it if (json_decode($result) !== NULL) return $result; // If it cannot be decoded: throw exception (don't cache it) throw new WireException("Found the data at $url, but it could not be decoded. Please check your API output!"); } // Throw exception if data could not be loaded in time (don't cache it) throw new WireException("Found the data at $url, but could not load it in time, after $attempt attempts. Result has type: " . gettype($result)); } // Throw generic exception if the requested page was not found or there was another error throw new WireException("Failed to load the content at: $url, with HTTP status: " . $status);
  13. An editor wanted to be able to preview the 'alternative page' we use during maintenance, but whenever he would try to view it he would be redirected to the homepage. So to me it makes sense that anyone who is allowed to bypass the maintenance page during maintenance, should also be able to preview the alternative page. Luckily, this is just a tiny code adjustment. Replace line 133-136 with: // Else if we're not in maintenance mode and we're not an administrator (and our role isn't in the list of allowed roles to access the site in maintenance mode), make sure the maintenance page redirects to the homepage } elseif (!$this->inMaintenance && $this->showPage && ($page->id == $this->showPage) && !$user->isSuperuser() && !array_intersect(explode('|', $user->roles), $this->bypassRoles)) { $this->session->redirect($this->config->urls->root, false); }
  14. I'm sorry @thomasaull, but I don't believe I understand the module's inner workings well enough to pull that off 😕 Here is the final cleaned-up and more secure 'api' template that I am using in between the RestApi router and ProCache, perhaps it can be of some help: Edit: check this reply for an updated version:
  15. Hi @thomas, no problem. And yes, definitely! I think that would be much better. Anything that comes from inside the regular page rendering can be cached with ProCache.
  16. eelkenet


    Hi @Wanze, congrats on your nice module! I'm currently testing it out for a client and find it overall very nice, thank you for your work! I have some feedback, even though I'm not sure if I'm understanding everything you do correctly. So please excuse me if I'm mistaken 😅 Having to set the default values for the meta tags on the field/template level, instead of using the PageTree (the Page level) seems unpractical. Most of the time I give a site editor access to the PageTree, sometimes to some modules, but I definitely keep them away from 'dangerous' stuff like Fields and Templates. However, editors should always be able to edit the SEO information without bugging the developer for an update. In other words, the preferred place for the default SEO information should imho be at the root of the tree: the homepage. (Or perhaps inside the a module config). To me it would make total sense that SEO information follows the same inheritance structure as pages do. Often rendered titles will consist of a page title and some ancestor title. So you should be able to select the parent's title and expand on it, much like the 'List of fields to display in the admin Page List') -home (title: "My Site") |-projects (title: "Projects") ||-project1 (title: "Project number 1") ||-project2 (title: "Another project") It would make sense to be able to get the following: -home (seo.meta.title: {title} would result in "My Site") |-projects (seo.meta.title: {title} - {parent.title} would result in "Projects - My Site") ||-project1 (seo.meta.title: {title} - {parent.parent.title} would result in "Project number 1 - My Site") ||-project2 (seo.meta.title: {title} | {parent.title} | {parent.parent.title} would result in "Another project | Projects | My Site")
  17. Ah, I did not know that. Makes sense as a limit for the full posts. For titles though.. not sure. Besides, Invision Community (the forum software) supports it on their own support forum just fine: https://invisioncommunity.com/search/?q=SEO
  18. While I know there are a couple of modules, topics and posts regarding SEO, it is impossible to search for these: for instance https://processwire.com/talk/search/?q=SEO&amp;type=forums_topic yields no results. It seems that the minimum input length is currently set at 4 characters, though this is not mentioned. So my request, if technically feasible: can the minimum search length please be reduced to 3 character long strings? If possible 2-letter combinations could be valuable as well, though that could be a bit too much of a strain on the database I guess.
  19. Congratulations on the launch, Ryan! I do have issues with the iMac/screenshots images though, as others have said before me. Not because it is an iMac, but because of the impossible size of the content. My first reaction as a visitor is to squint my eyes and to move my head close to the monitor to see the actual PW screenshots inside it. And to be honest I think that is a problem, because: The very first impression, the first thing people look at, on any website will always be graphics/images, not text. You only get one shot at a first impression The first impression should be very clear and very convincing The iMac frame and code in the background take about 75% of the available real estate, making the actual screenshots tiny! A simple sliding carousel or slideshow would suffice, possibly showing steps like: The pagetree Editing a page Cropping an image Creating fields Creating template
  20. Great work Ryan, thank you! I fully agree with this. Our main use of Processwire is as a headless CMS / CMF for Single Page Applications. As a possible future user I would be very interested in learning about how PW can be used in this way. I would look for that on the 'Output' page (https://processwire.com/newsite/docs/front-end/output/). A simple JSON API example, without the use of any external modules, would suffice here I think.
  21. I had a quite some trouble getting this to work with the content from a ProFields Table, as this does not work the same as with a Repeater or PageTable. But I am happy to say that treating those fields as a 'file' works, by setting this in the options array. Ie: <?php $finder = new RockFinder("template=template_name,limit=10", ["title", "some_field_name"]); $field = $finder->addField("my_table_field", ["column_name", "other_column"], ["type" => "file"]); $field->seperator = ", ";
  22. Hmm, that is weird, in that case it is probably being reset somewhere. Have you tried doing a full search of your project's code for places where it might be overridden? Maybe some module or template is accidentally setting it, instead of checking for the value.
  23. You should not overwrite this file. Instead, edit the config in site/config.php.
  24. I ran into this issue too. I edited the Mollie module to fit my own needs, a bit too many changes to create a merge request, but it deals with this issue. <?php namespace ProcessWire; // Here you can find more information about the Mollie php api https://github.com/mollie/mollie-api-php require_once __DIR__ . "/vendor/autoload.php"; class PaymentMollie extends PaymentModule { public static function getModuleInfo() { return [ 'title' => 'PaymentMollie', 'version' => '0.0.2', 'summary' => 'Mollie Payment method', 'singular' => false, 'autoload' => false, 'requires' => 'ProcessWire>=3.0.98, PaymentModule', ]; } public function init() { ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); } public function getTitle() { return $this->_("Mollie: iDeal, Bancontact"); } public function getFailureReason() { return "Error"; } public function processPayment() { $payment_id = wire()->input->post->id; $order_id = wire()->input->urlSegment2; $mollie = new \Mollie\Api\MollieApiClient(); $mollie->setApiKey($this->test_api_key); if ($this->live_check == "1") { $mollie->setApiKey($this->api_key); } $payment = $mollie->payments->get($payment_id); $order_id = $payment->metadata->order_id; wire()->log->save("mollie", "Processing payment for... $order_id"); if ($payment->isPaid() == true) { wire()->log->save("mollie", "Order $order_id was paid using mollie!"); $order = wire()->pages->get($order_id); $order->of(false); $order->pad_paid = time(); $order->addNote($this->_("Order paid using Mollie") . ": " . $payment->id); $order->removeStatus(Page::statusUnpublished); $order->save(); return true; } elseif ($payment->isOpen() == FALSE) { return false; } return false; } public function getMollieMethods() { $mollie = new \Mollie\Api\MollieApiClient(); $mollie->setApiKey($this->test_api_key); if ($this->live_check == "1") { $mollie->setApiKey($this->api_key); } $methods = $mollie->methods->all(); $return = [ ]; foreach ($methods as $method) { $return[] = [ "title" => htmlspecialchars($method->description), "icon" => htmlspecialchars($method->image->size1x), "icon2x" => htmlspecialchars($method->image->size2x), ]; } return $return; } public function render() { $output = ""; $mollie = new \Mollie\Api\MollieApiClient(); $mollie->setApiKey($this->test_api_key); if ($this->live_check == "1") { $mollie->setApiKey($this->api_key); } $paymentInfo = [ "amount" => [ "value" => number_format(($this->getTotalAmount() /100), 2, '.', ''), "currency" => 'EUR', ], "description" => "Bestelling van: " . $this->customer->givenName . " " . $this->customer->familyName, "redirectUrl" => $this->httphost . "checkout/?id=" . $this->id, "webhookUrl" => $this->httphost. "checkout/processmollie/" . $this->id . "/", // Specify here wat you want "metadata" => [ "order_id" => $this->id, /* "customer_name" => $this->customer->givenName, "customer_familyName" => $this->customer->familyName, "customer_address" => $this->customer->streetAddress, "customer_locality" => $this->customer->locality, "customer_postalCode" => $this->customer->postalCode, "customer_country" => $this->customer->country, "customer_email" => $this->customer->email */ ], ]; // $output .= "<pre>" . print_r($paymentInfo, true) . "</pre>"; $payment = $mollie->payments->create($paymentInfo); wire()->log->save("mollie", "Creating the payment... $order_id"); // Type here your custom HTML // use $payment->getPaymentUrl() as url for your payment link foreach ($methods as $method) { $output .= '<div style="line-height:40px; vertical-align:top">'; $output .= '<img src="' . htmlspecialchars($method->image->size1x) . '" srcset="' . htmlspecialchars($method->image->size2x) . ' 2x"> '; $output .= htmlspecialchars($method->description) . ' (' . htmlspecialchars($method->id) . ')'; $output .= '</div>'; } $output .= "</div>"; $output .= "<div><form action='{$payment->getCheckoutUrl()}'> <button type='submit'>". __('Pay') ."</button></form></div>"; return $output; } public static function getModuleConfigInputfields(array $data) { $inputfields = new InputfieldWrapper(); // Test API $live_api_field = wire('modules')->get('InputfieldText'); $live_api_field->name = 'test_api_key'; $live_api_field->label = __("Test API Key"); $live_api_field->notes = __(""); if (isset($data['test_api_key'])) { $live_api_field->value = $data['test_api_key']; } $inputfields->add($live_api_field); // Live API $test_api_field = wire('modules')->get('InputfieldText'); $test_api_field->name = 'api_key'; $test_api_field->label = __("Live API Key"); $test_api_field->notes = __(""); if (isset($data['api_key'])) { $test_api_field->value = $data['api_key']; } $inputfields->add($test_api_field); // Select live or test API $check_api_field = wire('modules')->get('InputfieldCheckbox'); $check_api_field->name = 'live_check'; $check_api_field->checked = $data['live_check']; $check_api_field->label = __("Use Live API Key"); $check_api_field->notes = __("If not selected, the test-api will be used. Keep in mind that you also have to activate the Live api on your Mollie dashboard"); if (isset($data['live_check'])) { $check_api_field->value = $data['live_check']; } $inputfields->add($check_api_field); // HTTP Host url $httphost_field = wire('modules')->get('InputfieldURL'); $httphost_field->name = 'httphost'; $httphost_field->label = __("HTTP Host"); $httphost_field->notes = __(""); if (isset($data['httphost'])) { $httphost_field->value = $data['httphost']; } $inputfields->add($httphost_field); return $inputfields; } }
  • Create New...